整体结构:
1.Serial收集器
(-XX:+UseSerialGC 复制算法)
1.是一个单线程收集器,只会使用一个CPU或一条收集线程去完成垃圾收集工作
2.进行垃圾收集时,必须暂停其他所有的工作线程,直至Serial收集器收集结束为止(“Stop The World”)
3.是HotSpot虚拟机运行在Client模式下的默认的新生代收集器
4.与其他收集器的单线程相比更加简单高效,对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,因此可以获得更高的单线程收集效率
2. ParNew收集器
(-XX:+UseParNewGC 复制算法)
1.ParNew收集器就是Serial收集器的多线程版本,它是一个新生代收集器
2.除了使用多线程进行垃圾收集外,其余行为与Serial收集器完全相同,是许多运行在Server模式下的虚拟机中首选的新生代收集器。目前只有它能与CMS收集器(老年代收集器)配合工作
3.ParNew在单cpu下性能比Serial差,但在多cpu下好,其默认开启与cpu数量相同的线程来进行垃圾收集
3.Parallel 收集器
(-XX:+UseParallelGC 复制算法)
(-XX:+UseParallelOldGC 标记-整理算法)
1.Parallel Scavenge收集器也是一个并行的多线程新生代收集器
2.用复制算法,目的是为了达到一个可控制的吞吐量
1.Parallel Old收集器是用在老年代的收集器,使用标记整理算法
吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值:吞吐量=运行用户代码的时间/(运行用户代码时间+垃圾收集时间)
4. CMS收集器
(-XX:+UseConcMarkSweepGC 标记-清除算法)
概述:
CMS,全称Concurrent Mark and Sweep,用于对年老代进行回收,目标是尽量减少应用的暂停时间,减少full gc发生的机率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代
CMS并非没有暂停,而是用两次短暂停来替代串行标记整理算法的长暂停。
收集周期:
1) Initial Mark 初始标记
这个阶段的任务是标记老年代中被GC Roots直接可达和被年轻代对象引用的对象,这个阶段也是第一次STW发生的阶段
2) Concurrent Mark 并发标记
这个阶段主要是通过从初始标记阶段中寻找到的标记对象开始,遍历老年代并且标记所有存活着的对象
需要注意的是,并非所有在老年代中存活的对象都会被标记,因为程序在标记期间可能会更改引用(比如图中的Current obj,它是并发标记阶段伴随着程序一起被删除了引用的对象)
这个阶段与应用程序共同运行
3) Concurrent Preclean 执行预清理
注: 相当于两次 concurrent-mark. 因为上一次concurrent-mark耗时较长,会有从新生代晋升到老年代的对象出现,将其清理掉
这也是一个并发阶段,与应用程序的线程并行执行。并发标记阶段与应用程序同时运行时,一些对象的引用可能会被改变,一旦这种情况发生,JVM就会标记堆上面的这块包含了变化对象的区域(这个堆的区域被称为"Card",这种方式被称为"Card Marking")
在这个阶段,这些脏对象将会被声明,并且这些对象能够到达的对象也会被标记。这些Card将会在上面的工作完成之后被清理掉
此外,还将执行一些必要的整理和重新标记阶段的准备工作。
4) Concurrent Abortable Preclean 执行可中止预清理
这个阶段也是和程序线程并发执行的。它的工作就是尽可能地进行清理工作,以减少重新标记阶段的任务(即减少了STW的停顿时间)
这个阶段的持续时间取决于很多因素,因为它需要不断地做一些相同的工作,直到满足某个终止条件为止(比如一定的迭代次数、一定的有效工作量、一定的时间等等)
5) Final Remark 重新标记
这个阶段是第二次,也是最后一次STW。这个阶段的目的是标记在老年代中被标记的所有存活下来的对象。
6) Concurrent Sweep 并发清除
移除未使用的对象,并且回收其占用的空间。
7) Concurrent Reset 并发重置
重置CMS算法内部的数据结构,为下一个周期做准备
参考文档:https://plumbr.io/handbook/garbage-collection-algorithms-implementations/concurrent-mark-and-sweep
CMS减少停顿的原理:
标记过程分三步:并发标记是最主要的标记过程,而这个过程是并发执行的,可以与应用程序线程同时进行,初始标记和重新标记虽然不能和应用程序并发执行,但这两个过程标记速度快,时间短,所以对应用程序不会产生太大的影响
最后并发清除的过程,也是和应用程序同时进行的,避免了应用程序的停顿。
CMS的特点:减少了应用程序的停顿时间,让回收线程和应用程序线程可以并发执行,它的回收并不彻底。因此CMS回收的频率相较其他回收器要高,频繁的回收将影响应用程序的吞吐量,空间碎片多。
CMS何时开始?
cms gc 通过一个后台线程触发,该线程随着堆一起初始化,触发机制是默认每隔2秒判断一下当前老年代的内存使用率是否达到阈值,如果高于某个阈值的时候将激发CMS。
两次STW的原因:
当虚拟机完成两次标记后,便确认了可以回收的对象。但是,垃圾回收并不会阻塞程序的线程,如果当GC线程标记好了一个对象的时候,此时程序的线程又将该对象重新加入了GC-Roots的“关系网”中,当执行二次标记的时候,该对象也没有重写finalize()方法,因此回收的时候就会回收这个不该回收的对象。
为了解决这个问题,虚拟机会在一些特定指令位置设置一些“安全点”,当程序运行到这些“安全点”的时候就会暂停所有当前运行的线程(Stop The World 所以叫STW),暂停后再找到“GC Roots”进行关系的组建,进而执行标记和清除。
这些特定的指令位置主要在:
1、循环的末尾
2、方法临返回前 / 调用方法的call指令后
3、可能抛异常的位置
5.G1收集器
(-XX:+UseG1GC 复制+标记-整理算法)
概述:
是jdk1.7以后推出的回收器,试图取代CMS回收器
不同于其他的回收器,G1将堆空间划分成了互相独立的区块。每块区域既有可能属于老年代、也有可能是新生代,并且每类区域空间可以是不连续的(对比CMS的老年代和新生代都必须是连续的)
这种将老年代区划分成多块的理念源于:当并发后台线程寻找可回收的对象时、有些区块包含可回收的对象要比其他区块多很多。虽然在清理这些区块时G1仍然需要暂停应用线程、但可以用相对较少的时间优先回收包含垃圾最多区块。
G1相对CMS回收器来说区别在于:
1、CMS是以获取最短回收停顿时间为目标,G1是面向服务端应用的垃圾收集器
1、因为划分了很多区块,回收时减小了内存碎片的产生
2、G1适用于新生代和老年代,而CMS只适用于老年代
3、CMS使用标记清除算法,G1整体上看是“标记-整理”,局部看是“复制”