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直接可达和被年轻代对象引用的对象( The latter is important since the Old Generation is collected separately.),速度很快
这个阶段也是第一次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。这个阶段的目的是标记在老年代中所有存活下来的对象(和着3、4一起完成修复并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录的任务)
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.循环跳转
3.异常跳转
附:HotSpot对象存活判定和垃圾回收算法的实现(是如何发起内存回收的)
CMS的缺点:
CPU资源敏感:CMS默认回收线程数是(CPU数量+3)/4,CPU数量多还好,数量低了的话,就会占用较多的CPU资源,影响用户程序执行速度
无法清除浮动垃圾:可能出现“Concurrent Mode Failure”失败而导致另一次Full GC(浮动垃圾:由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理的这一部分垃圾)
空间碎片:基于标记清除算法会产生大量空间碎片,会对大对象的分配带来麻烦,可能会提前触发Full GC。CMS会在顶不住快进入Full GC的时候进行内存碎片的合并整理,还会在每次进入Full GC的时候都进行一次碎片整理。
5.G1收集器
(-XX:+UseG1GC 复制+标记-整理算法)
概述:
是jdk1.7以后推出的回收器,解决了CMS垃圾收集时产生的空间碎片,试图取代CMS回收器
不同于其他的回收器,G1将堆空间划分成了互相独立的区块。每块区域既有可能属于老年代、也有可能是新生代,并且每类区域空间可以是不连续的(对比CMS的老年代和新生代都必须是连续的)
这种将老年代区划分成多块的理念源于:当并发后台线程寻找可回收的对象时、有些区块包含可回收的对象要比其他区块多很多。虽然在清理这些区块时G1仍然需要暂停应用线程、但可以用相对较少的时间优先回收包含垃圾最多区块。
G1相对CMS回收器来说区别在于:
1、G1能够提供可预测的垃圾回收停顿时间,也可以自己设置相对应的停顿时间
2、G1适用于新生代和老年代,而CMS只适用于老年代
3、CMS使用标记清除算法,G1整体上看是“标记-整理”,局部看是“复制”,这两种算法都能提供规整的内存空间,从而避免空间碎片的产生
内存结构:
每个Region被标记了E、S、O和H,说明每个Region在运行时都将存储不同的对象。
其中H是以往算法中没有的,它代表Humongous,这表示这些Region存储的是大对象(humongous object,H-obj),当新建对象大小超过Region大小一半时,直接在新的一个或多个连续Region中分配,并标记为H。
G1将对象从堆的一个或多个区域复制到堆上的单个区域,并且在此过程中压缩并释放内存。这种空间分配的动作在多处理器上并行执行,以减少暂停时间并提高吞吐量。因此,每次垃圾收集,G1都会不断努力减少碎片。
GC模式
G1提供三种垃圾回收模式:young gc、mixed gc 和 full gc,在不同的条件下被触发。
young gc
发生在年轻代的GC算法,一般对象(除了大对象)都是在eden region中分配内存,当所有eden region被耗尽无法申请内存时,就会触发一次young gc,这种触发机制和之前的young gc差不多,执行完一次young gc,活跃对象会被拷贝到survivor region或者晋升到old region中,空闲的region会被放入空闲列表中,等待下次被使用。
mixed gc
当越来越多的对象晋升到老年代old region时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即mixed gc,该算法并不是一个old gc,除了回收整个young region,还会回收一部分的old region,这里需要注意:是一部分老年代,而不是全部老年代,可以选择哪些old region进行收集,从而可以对垃圾回收的耗时时间进行控制。
mixed gc的执行过程有点类似cms,主要分为以下几个步骤:
initial mark: 初始标记过程,整个过程STW,标记了从GC Root可达的对象
concurrent marking: 并发标记过程,整个过程gc collector线程与应用线程可以并行执行,标记出GC Root可达对象衍生出去的存活对象,并收集各个Region的存活对象信息
remark: 最终标记过程,整个过程STW,标记出那些在并发标记过程中遗漏的,或者内部引用发生变化的对象
clean up: 垃圾清除过程,如果发现一个Region中没有存活对象,则把该Region加入到空闲列表中。
full gc
如果对象内存分配速度过快,mixed gc来不及回收,导致老年代被填满,就会触发一次full gc,G1的full gc算法就是单线程执行的serial old gc,会导致异常长时间的暂停时间,需要进行不断的调优,尽可能的避免full gc.
G1大部分内容来自于:
https://www.jianshu.com/p/0f1f5adffdc1
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc.html