GC 垃圾收集器层层剖析

1.你知道哪些垃圾收集器?

新生代收集器:

  • 串行-**收集器 **Serial (单线程、复制算法) [-XX:+UseSerialGC]
  • ParNew (Serial+多线程 , 复制算法)[-XX:+UseParNewGC]
    • ParallelScavenge [-XX:+UseParallelGC]
  • G1 (多线程标记清除算法)

老年代收集器:

  • SerialOld(单线程、标记整理算法) [ -XX:+UseSerialOldGC ]
  • ParallelOld(多线程标记整理算法) [ -XX:+UseParallelOldGC]
  • CMS(-XX:+UseConcMarkSweepGC)(多线程标记清除法)
  • G1(多线程标记整理算法) [-XX:+UseG1GC]

G1是一个独立的收集器不依。ZGC是目前JDK 11的实验收集器。

2.你们项目中用的哪些

  • 老年代用的 cms
  • 新生代用的 ParNew (使用复制算法完成垃圾收集)

3.CMS的流程

  • 初始标记(STW initial mark):
    • 从垃圾回收的"根对象"开始,只扫描到能够和"根对象"直接关联的对象,并作标记。所以这个过程虽然暂停了整个JVM,但是很快就完成了。(标记GC Roots存活对象)
  • 并发标记(Concurrent marking):这个阶段紧随初始标记阶段,在初始标记的基础上继续向下追溯标记。并发标记阶段,应用程序的线程和并发标记的线程并发执行,所以用户不会感受到停顿。(对GC Roots对象引用进行追踪,标记存活)
  • 并发预清理(Concurrent precleaning):并发预清理阶段仍然是并发的。在这个阶段,虚拟机查找在执行并发标记阶段新进入老年代的对象(可能会有一些对象从新生代晋升到老年代, 或者有一些对象被分配到老年代)。通过重新扫描,减少下一个阶段"重新标记"的工作,因为下一个阶段会Stop The World。
  • 重新标记(STW remark):这个阶段会暂停虚拟机,收集器线程扫描在CMS堆中剩余的对象。扫描从"跟对象"开始向下追溯,并处理对象关联。
  • 并发清理(Concurrent sweeping):清理垃圾对象,这个阶段收集器线程和应用程序线程并发执行。
  • 并发重置(Concurrent reset):这个阶段,重置CMS收集器的数据结构状态,等待下一次垃圾回收。

4.CMS的缺点

CMS采用 标记-清理 的算法**

  • 无法处理浮动垃圾,并发收集会造成内存碎片过多
  • 由于并发标记和并发清理阶段都是并发执行,所以会额外消耗CPU资源

解决老年代碎片化问题的办法
CMS在进行一定次数的Full GC(标记清除)的时候进行一次标记整理算法,

-XX:UseCMSCompactAtFullCollection -XX:CMSFullGCBeforeCompaction=5

5.G1与CMS的区别

  • 使用范围不一样
    • CMS是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用
    • G1收集范围是老年代和新生代。不需要结合其他收集器使用
  • 回收垃圾的时间( stop the world )
    • CMS以最小的停顿时间为目标的收集器。
    • G1可预测垃圾回收的停顿时间(建立可预测的停顿时间模型)
  • 垃圾碎片
    • CMS使用“标记-清除”算法进行的垃圾回收,容易产生内存碎片
    • G1使用的是“标记-整理”算法,进行了空间整合,降低了内存空间碎片。
  • 垃圾回收的过程不一样
    • CMS: 初始标记、并发标记、并发预清理、重新标记、并发清理、并发重置
    • G1 : 初始标记、并发标记、最终标记、筛选回收
  • 在大对象的处理上
    • CMS中若一个大对象,进入S1、S2区域的时候大于改分配的区域,对象会直接进入老年代
    • G1处理大对象时会判断对象是否大于一个Region大小的50%,如果大于50%就会横跨多个Region进行存放

附录:详细介绍

1.Serial (单线程、复制算法)[ -XX:+UseSerialGC ]

  • 用于新生代
  • 单线程、最基本的垃圾回收器,使用复制算法
  • 只使用一个CPU或一个线程去完成垃圾收集工作,并且在进行垃圾收集的同时,需暂停其他所有的工作线程,直到垃圾收集结束。
  • 配置参数:

** 收集过程**

  • 使用复制算法进行回收
  • 将eden和from survivor 区活跃的对象复制到to survivor区,并清空eden区和from survivor区,
  • 如果to survivor区满了,那么部分对象将会被晋升移动到老年代,随后交换from和to区。

** 优缺点:**

  • 单线程地好处就是减少上下文切换,减少系统资源的开销。
  • 但在GC的过程中,会暂停程序的执行。若GC不是频繁发生,这或许是一个不错的选择,否则将会影响程序的执行性能。 对于新生代来说,区域比较小,停顿时间短,所以比较使用。

2.Serial Old(单线程、标记整理算法 )

  • Serial 垃圾收集器,用于年老代的版本。
  • 单线程、最基本的垃圾回收器,使用标记-压缩-清理算法

在回收阶段,将标记对象越过堆的空闲区移动到堆的另一端,所有被移动的对象的引用也会被更新指向新的位置。

3.ParNew (Serial+多线程 , 复制算法 )[ -XX:+UseParNewGC ]

  • ParNew收集器是Serial收集器的多线程版本
  • 使用复制算法完成垃圾收集
  • 默认开启的收集线程数与CPU的数量相同,可使用 XX:ParallelGCThreads 参数来限制垃圾收集的线程数
  • 并行仅仅指的是收集多线程并行, ParNew 垃圾收集器在垃圾收集过程中同样也要短暂暂停所有其他的工作线程。
  • 可与CMS收集器配合工作。

4.Parallel (多线程复制算法、高效 ) [ -XX:+UseParallelGC ]

  • 通过多线程完成垃圾的清理工作
  • 使用复制算法完成垃圾收集
  • 占用较低的CPU因而能提高应用的吞吐

5.ParallelOld (多线程标记整理算法)[ -XX:+UseParallelOldGC ]

  • 多个线程来完成
  • 使用算标记 - 整理算法
  • JDK1.6之后才开始提供,是一个老年代收集器
  • 能保证新生代的吞吐量优先,无法保证整体的吞吐量

通过目标参数-XX:MaxGCPauseMills和-XX:GCTimeRatio,调整新生代空间大小,来降低GC触发的频率。
并行收集器适合对吞吐量要求远远高于延迟要求的场景,并且在满足最差延时的情况下,并行收集器将提供最佳的吞吐量。

如果系统对吞吐量要求比较高,可以优先考虑新生代 Parallel Scavenge
和年老代 Parallel Old 收集器的搭配策略。

6.CMS(多线程标记清除算法)

  • 针对老年代的多线程收集器
  • 实现了让垃圾收集器与用户线程(近似)同时工作

CMS(Concurrent Mark Sweep)收集器具有以下特点:

  • 基于"标记-清除"算法;
  • 以获取最短回收停顿时间为目标;
  • 并发收集,停顿时间短。

CMS执行过程:

  • 初始标记(STW initial mark):
    • 从垃圾回收的"根对象"开始,只扫描到能够和"根对象"直接关联的对象,并作标记。所以这个过程虽然暂停了整个JVM,但是很快就完成了。(标记GC Roots存活对象)
  • 并发标记(Concurrent marking):这个阶段紧随初始标记阶段,在初始标记的基础上继续向下追溯标记。并发标记阶段,应用程序的线程和并发标记的线程并发执行,所以用户不会感受到停顿。(对GC Roots对象引用进行追踪,标记存活)
  • 并发预清理(Concurrent precleaning):并发预清理阶段仍然是并发的。在这个阶段,虚拟机查找在执行并发标记阶段新进入老年代的对象(可能会有一些对象从新生代晋升到老年代, 或者有一些对象被分配到老年代)。通过重新扫描,减少下一个阶段"重新标记"的工作,因为下一个阶段会Stop The World。
  • 重新标记(STW remark):这个阶段会暂停虚拟机,收集器线程扫描在CMS堆中剩余的对象。扫描从"跟对象"开始向下追溯,并处理对象关联。
  • 并发清理(Concurrent sweeping):清理垃圾对象,这个阶段收集器线程和应用程序线程并发执行。
  • 并发重置(Concurrent reset):这个阶段,重置CMS收集器的数据结构状态,等待下一次垃圾回收。

内存碎片问题:
-XX:UseCMSCoimpactAtFullCollection 默认打开,在cms fgc后会STW进行碎片整理。
-XX:CMSFullGCsBeforeCompaction=0 默认0,多少次fgc后进行一次碎片整理,0每次fgc后都整理。

concurrent mode failure 问题:
CMS并发处理中,年轻代移到到老年代内存不够,老年代的垃圾收集器从CMS退化为Serial Old,所有应用线程被暂停。
-XX:CMSInitiatingOccupancyFraction=N调小,并启用碎片整理

CMS 也有一些缺点,其中最大的问题就是老年代内存碎片问题(因为不压缩),
在某些情况下 GC 会造成不可预测的暂停时间,特别是堆内存较大的情况下。

7.G1 收集器

G1收集器概述
JDK1.7后全新的JVM垃圾收集器G1收集器, 目标用于取代CMS收集器。

  1. G1收集器的最大特点
  • G1最大的特点是引入分区的思路,弱化了分代的概念。
  • 合理利用垃圾收集各个周期的资源,解决了其他收集器甚至CMS的众多缺陷。

2. G1相比较CMS的改进

  • 算法: G1基于标记-整理算法, 不会产生空间碎片,分配大对象时不会无法得到连续的空间而提前触发一次FULL GC。
  • 停顿时间可控: G1可以通过设置预期停顿时间(Pause Time)来控制垃圾收集时间避免应用雪崩现象。
  • 并行与并发:G1能更充分的利用CPU,多核环境下的硬件优势来缩短stop the world的停顿时间。

3. CMS和G1的区别

  • CMS中,堆被分为PermGen,YoungGen,OldGen;而YoungGen又分了两个survivo区域。在G1中,堆被平均分成几个区域(region),在每个区域中,虽然也保留了新老代的概念,但是收集器是以整个区域为单位收集的。
  • G1在回收内存后会马上同时做合并空闲内存的工作、而CMS默认是在STW(stop the world)的时候做。
  • G1会在Young GC中使用、而CMS只能在O区使用。

4. G1收集器的应用场景
G1垃圾收集算法主要应用在多CPU大内存的服务中,在满足高吞吐量的同时,尽可能的满足垃圾回收时的暂停时间。
就目前而言、CMS还是默认首选的GC策略、可能在以下场景下G1更适合:

  • 服务端多核CPU、JVM内存占用较大的应用(至少大于4G)
  • 应用在运行过程中会产生大量内存碎片、需要经常压缩空间
  • 想要更可控、可预期的GC停顿周期,防止高并发下应用雪崩现象

参考:https://zhuanlan.zhihu.com/p/59861022

Garbage first 垃圾收集器是目前垃圾收集器理论发展的最前沿成果,相比与 CMS 收集器,G1 收
集器两个最突出的改进是:

  • 基于标记-整理算法,不产生内存碎片。
  • 可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。

G1 收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域
的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间,优先回收垃圾
最多的区域。区域划分和优先级区域回收机制,确保 G1 收集器可以在有限时间获得最高的垃圾收
集效率。

上一篇:JAVA jvm 调优经历简单记录


下一篇:2021年G1工业锅炉司炉新版试题及G1工业锅炉司炉模拟考试题