写点什么

必知必会 JVM 四 - 垃圾收集器介绍,linux 驱动开发入门与实践

用户头像
极客good
关注
发布于: 刚刚

<table cellspacing="0"><tbody><tr><td><p><strong>收集器</strong></p></td><td><p><strong>收集对象和算法</strong></p></td><td><p><strong>收集器类型</strong></p></td></tr><tr><td><p>Serial</p></td><td><p>新生代,复制算法</p></td><td><p>单线程</p></td></tr><tr><td><p>ParNew</p></td><td><p>新生代,复制算法</p></td><td><p>并行的多线程收集器</p></td></tr><tr><td><p>Parallel Scavenge</p></td><td><p>新生代,复制算法</p></td><td><p>并行的多线程收集器</p></td></tr><tr><td><p><strong>Serial Old</strong></p></td><td><p><strong>老年代,标记整理算法</strong></p></td><td><p><strong>单线程</strong></p></td></tr><tr><td><p>Parallel Old</p></td><td><p>老年代,标记整理算法</p></td><td><p>并行的多线程收集器</p></td></tr><tr><td><p>CMS</p></td><td><p>老年代,标记清除算法</p></td><td><p>并行与并发收集器</p></td></tr><tr><td><p>G1</p></td><td><p>跨新生代和老年代;标记整理 + 化整为零</p></td><td><p>并行与并发收集器</p></td></tr></tbody></table>


三、垃圾收集器介绍


=========


3.1 Serial/Serial Old




最古老的,单线程,独占式,成熟,适合单 CPU? 服务器


-XX:+UseSerialGC 新生代和老年代都用串行收集器


-XX:+UseParNewGC 新生代使用 ParNew,老年代使用 Serial Old


-XX:+UseParallelGC 新生代使用 ParallerGC,老年代使用 Serial Old


3.2 ParNew




和 Serial 基本没区别,唯一的区别:多线程,多 CPU 的,停顿时间比 Serial 少


-XX:+UseParNewGC 新生代使用 ParNew,老年代使用 Serial Old


除了性能原因外,主要是因为除了 Serial 收集器,只有它能与 CMS 收集器配合工作。


3.3 Paralle


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


l Scavenge(ParallerGC)/Parallel Old




关注吞吐量的垃圾收集器,高吞吐量则可以高效率地利用 CPU 时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。


所谓吞吐量就是 CPU 用于运行用户代码的时间与 CPU 总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机总共运行了 100 分钟,其中垃圾收集花掉 1 分钟,那有吞吐效率就是 99%。


3.4 Concurrent Mark Sweep (CMS)




收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的 Java 应用集中在互联网站或者 B/S 系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。CMS 收集器就非常符合这类应用的需求。


整个过程分为 4 个步骤,包括:


  1. **初始标记:**仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快,需要停顿(STW -Stop the world)。

  2. **并发标记:**从 GC Root 开始对堆中对象进行可达性分析,找到存活对象,它在整个回收过程中耗时最长,不需要停顿。

  3. **重新标记:**为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,需要停顿(STW)。这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。

  4. **并发清除:**不需要停顿。



优点:


由于整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以,从总体上来说,CMS 收集器的内存回收过程是与用户线程一起并发执行的。


缺点:


CPU 资源敏感:因为并发阶段多线程占据 CPU 资源,如果 CPU 资源不足,效率会明显降低。


**浮动垃圾:**由于 CMS 并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS 无法在当次收集中处理掉它们,只好留待下一次 GC 时再清理掉。这一部分垃圾就称为“浮动垃圾”。


由于浮动垃圾的存在,因此需要预留出一部分内存,意味着 CMS 收集不能像其它收集器那样等待老年代快满的时候再回收。


在 1.6 的版本中老年代空间使用率阈值(92%)


如果预留的内存不够存放浮动垃圾,就会出现 Concurrent Mode Failure,这时虚拟机将临时启用 Serial Old 来替代 CMS。


**会产生空间碎片:**标记 - 清除算法会导致产生不连续的空间碎片


3.5 G1




G1 垃圾回收器是主要针对多处理器以及大内存的机器,以极高的概率满足预测 GC 停顿时间要求的同时,还具备高吞吐量性能特征。是基于标记整理的垃圾回收器。

3.5.1 G1 堆管理方式

堆内存被划分为多个大小相等的逻辑 heap 区,其中一部分区域被当成老一代收集器相同的角色(eden, survivor, old), 但每个角色的区域个数都不是固定的。



每个堆 Region 的大小在 JVM 启动时就确定了,JVM 通常生成 2000 个 Region,每个 Region 大小在 1M-32M 之间。这些 Region 会被逻辑映射成 Eden, Survivor, 和 old generation(老年代)空间。

3.5.2 G1 新生代垃圾回收

新生代中存活的对象被转移到一个或者多个 Survivor 区,如果达到存活阀值则这部分对象就会被迁移到 old 老年区。在回收过程中会有一次 STW 暂停,会计算 Eden 和 Survivor 大小给下一次 GC 使用。


新生代回收之前:



新生代回收结束后:新生代被转移到 Survivor 区或者 old 区,并行垃圾收集。


3.5.3 G1 老年代垃圾回收

  1. 初始标记(initial mark,STW)


在此阶段,G1 GC 对根进行标记。该阶段与常规的 (STW) 年轻代垃圾回收密切相关。


  1. 根区域扫描(root region scan)


G1 GC 在初始标记的存活区扫描对老年代的引用,并标记被引用的对象。该阶段与应用程序(非 STW)同时运行,并且只有完成该阶段后,才能开始下一次 STW 年轻代垃圾回收。


  1. 并发标记(Concurrent Marking)


G1 GC 在整个堆中查找可访问的(存活的)对象。该阶段与应用程序同时运行,可以被 STW 年轻代垃圾回收中断


  1. 最终标记(Remark,STW)


该阶段是 STW 回收,帮助完成标记周期。G1 GC 清空 SATB 缓冲区,跟踪未被访问的存活对象,并执行引用处理。


  1. 清除垃圾(Cleanup,STW)


在这个最后阶段,G1 GC 执行统计和 RSet 净化的 STW 操作。在统计期间,G1 GC 会识别完全空闲的区域和可供进行混合垃圾回收的区域。清理阶段在将空白区域重置并返回到空闲列表时为部分并发。


3.6?ZGC




Java11 已经推出最新垃圾收集器,ZGC 主要为了减少 JVM 停顿时间。


ZGC 全称是 Z Garbage Collector,是一款可伸缩(scalable)的低延迟(low latency garbage)、并发(concurrent)垃圾回收器,旨在实现以下几个目标:


  • 停顿时间不超过 10ms

  • 停顿时间不随 heap 大小或存活对象大小增大而增大

  • 可以处理从几百兆到几 T 的内存大小(最大 4T)

[](

)[](


)3.6.1 主要实现技术


指针标记(Pointer tagging Or Colored Pointers )


ZGC 利用指针的 64 位中的几位表示 Finalizable、Remapped、Marked1、Marked0,以标记该指向内存的存储状态。相当于在对象的引用上标注了对象的信息(不是对象头)。在这个被指向的内存发生变化的时候(内存在 Compact 整理被移动时),颜色就会发生变化。



  • Marked0/marked1: 判断对象是否已标记

  • Remapped: 判断应用是否已指向新的地址

  • Finalizable: 判断对象是否只能被 Finalizer 访问


这几个 bits 在不同的状态也就代表这个引用的不同颜色


为什么有 2 个 mark 标记?每一个 GC 周期开始时,会交换使用的标记位,使上次 GC 周期中修正的已标记状态失效,所有引用都变成未标记。


  • GC 周期 1:使用 mark0, 则周期结束所有引用 mark 标记都会成为 01。

  • GC 周期 2:使用 mark1, 则期待的 mark 标记 10,所有引用都能被重新标记。


GC 屏障 (GC Barriers)


由于着色指针的存在,在程序运行时访问对象的时候,可以轻易知道对象在内存的存储状态(通过指针访问对象),若请求读的内存在被着色了,那么则会触发读屏障,读屏障会更新指针再返回结果,此过程有一定的耗费,从而达到与用户线程并发的效果。


与标记对象的传统算法相比,ZGC 在指针上做标记,在访问指针时加入 Load Barrier(读屏障),比如当对象正被 GC 移动,指针上的颜色就会不对,这个屏障就会先把指针更新为有效地址再返回,也就是,永远只有单个对象读取时有概率被减速,而不存在为了保持应用与 GC 一致而粗暴整体的 Stop The World。

[](

)[](


)3.6.2?ZGC 原理

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
必知必会JVM四-垃圾收集器介绍,linux驱动开发入门与实践