垃圾回收集器

有些话、适合烂在心里 提交于 2020-03-06 13:57:46

1.垃圾收集器

G1收集器

首先将Java堆空间划分为一些大小相等的区域(region),每个区域都是虚拟机中的一段连续内存空间。G1通过执行并发的全局标记来确定整个Java堆空间中存活的对象。标记阶段完成后,G1就知道哪些区域基本上是空闲的。在回收内存时优先回收这些区域,这样通常都会回收相当数量的内存。这就是为什么它叫做Garbage-First的原因。顾名思义G1关注某些区域的回收和整理,这些区域中的对象很有可能被完全回收。而且G1使用了一个暂停时间预测模型使得暂停时间控制在用户指定的暂停时间内,并根据用户指定的暂停时间来选择合适的区域回收内存。它与前面的CMS收集器相比有两个显著的改进:一是G1收集器是基于“标记-整理”算法实现的收集器,也就是说它不会产生空间碎片,这对于长时间运行的应用系统来说非常重要。二是它可以非常精确地控制停顿,既能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,具备了一些实时Java(RTSJ)的垃圾收集器的特征。
如果你的堆内存大于4G的话,那么G1会是要考虑使用的收集器。它是为了更好支持大于4G堆内存在JDK 7 u4引入的。G1收集器把堆分成多个区域,大小从1MB到32MB,并使用多个后台线程来扫描这些区域,优先会扫描最多垃圾的区域,这就是它名称的由来,垃圾优先Garbage First。

串行收集器Seiral Collector

串行收集器是最简单的,它设计为在单核的环境下工作(32位或者windows),你几乎不会使用到它。它在工作的时候会暂停整个应用的运行,因此在所有服务器环境下都不可能被使用。Serial收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅是说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。使用方法:-XX:+UseSerialGC

并行垃圾收集器

并行收集器可以理解是多线程串行收集,在串行收集基础上采用多线程方式进行GC,很好的弥补了串行收集的不足,可以大幅缩短停顿时间(如下图表示的停顿时长高度,并发比并行要短),因此对于空间不大的区域(如young generation),采用并行收集器停顿时间很短,回收效率高,适合高频率执行。
使用方法:-XX:+UseParallelGC

CMS收集器

    CMS收集器是为了低延迟而生,通过尽可能的并行执行垃圾回收的几个阶段来把延迟控制到最低。CMS收集器是老年代的垃圾收集器,一般情况下会有ParNew来配合执行(默认情况下也是ParNew),ParNew也是使用并行的算法来执行年轻代的回收。当然除此之外,你还可以选择使用Serial收集器来收集年轻代,不过一般很少这样使用。通过咱们说的CMS收集器是指广义上的CMS收集器,包含以下几个:ParNew(Young)GC + CMS(Old)GC + Serial GC 算法(应对核心的CMS GC某些时候的不赶趟,开销很大)。本文重点介绍一些CMS在Old区域的回收。

触发条件

    CMS垃圾收集器的触发条件有以下几个:
1、如果没有设置-XX:+UseCMSInitiatingOccupancyOnly,虚拟机会根据收集的数据决定是否触发(建议带上这个参数)。
2、老年代使用率达到阈值 CMSInitiatingOccupancyFraction,默认92%,前提是配置了第一个参数。
3、永久代的使用率达到阈值 CMSInitiatingPermOccupancyFraction,默认92%,前提是开启 CMSClassUnloadingEnabled并且配置了第一个参数。
4、新生代的晋升担保失败。

CMS的收集阶段

    CMS收集器在收集老年代的时候分为以下几个阶段:初始标记、并发标记、预清理、可中断预清理、最终标记、并发清除、并发重置。每个阶段的运行过程如下:
       
    下面分别从这几个阶段来介绍CMS收集器。

初始标记

    初始标记阶段主要做两件事:一是遍历GCRoot可直达的老年代对象;二是遍历新生代直达的老年代对象。这里的直达是指直接关联到GCRoot的一级对象。初始标记阶段是完全STW的,引用程序会暂停。通过-XX:+CMSParallelInitialMarkEnabled参数可以开启该阶段的并行标记,使用多个线程进行标记,减少暂停时间。
    哪些对象可以作为GCRoot:
1、所有Java线程当前栈帧引用的,也就是正在被调用的方法的引用类型的参数、局部变量以及临时值。
2、所有的静态数据结构引用的对象
3、String常量池里的引用
4、运行时常量池里引用的类型

并发标记

    并发标记阶段是与应用程序一起执行的,这个阶段主要做两件事:
1、对初始标记中标记的存活对象进行trace,标记这些对象为可达对象,例如A->B,A在初始标记被识别,而B就是在并发标记阶段被识别。
2、将在并发阶段新生代晋升到老年代的对象、直接在老年代分配的对象以及老年代引用关系发生变化的对象所在的card标记为dirty,避免在重新标记阶段扫描整个老年代。
因为并发标记阶段与引用程序一起执行,因此会出现之前A->B->C变成A->C的情况,这种情况下C对象时无法在并发标记阶段被标记的。在标记阶段会使用三色标记算法。
三色标记法把 GC 中的对象划分成三种情况:
白色:还没有搜索过的对象(白色对象会被当成垃圾对象)
灰色:正在搜索的对象
黑色:搜索完成的对象(不会当成垃圾对象,不会被 GC)
假设刚开始的对象图如下:


在并发标记阶段,B对象引用C对象变为A对象引用C对象,如下


这时候再扫描的时候就会变成如下的图


此时C对象变成了白色的,但是显然是不正确的,于是就有两种方式来处理这种情况:
1、在对象引用发生变化之前记录对象引用关系
2、在对象引用发生变化之后记录对象引用关系
    这两种思路正好对应了CMS和G1的两种处理方式。在CMS采用的是增量更新(Incremental update),只要在写屏障(write barrier)里发现要有一个白对象的引用被赋值到一个黑对象 的字段里,那就把这个白对象变成灰色的。即插入的时候记录下来。在G1中,使用的是STAB(snapshot-at-the-beginning)的方式,删除的时候记录所有的对象

预清理

    通过参数CMSPrecleaningEnabled选择关闭该阶段,默认启用,主要做两件事情
1、并发标记阶段在Eden分配了对象A,并且A引用了老年代对象B,那么这个阶段标记B为活跃对象
2、扫描并发标记阶段的Dirty Card,重新标记那些在并发标记阶段引用被更新的对象

可中断预清理

    该阶段存在的目的是减轻重新标记的工作量,减少暂停时间,主要做两件事情:
1、扫描处理DirtyCard中的对象
2、处理新生代引用到的老年代的对象
该阶段退出的条件有三个:
1、CMSMaxAbortablePrecleanTime参数控制的5秒退出
2、Eden区达到CMSScheduleRemarkEdenPenetration参数配置的值(默认50%)
3、CMSMaxAbortablePrecleanLoops控制的扫描次数(默认是0,不退出)
该阶段是希望能发生一次Young GC,这样就可以减少Eden区对象的数量,降低重新标记的工作量,因为重新标记会扫描整个Eden区的

最终标记

    最终标记又叫重新标记,该阶段也是STW的,主要会遍历三个地方:
1、遍历Eden区,重新标记
2、遍历DirtyCard,重新标记
3、遍历GC Root,重新标记
由于该阶段遍历的区域很多,因此有可能会耗时比较长,并且该阶段是完全的STW的。
通过CMSScavengeBeforeRemark参数可以强制在重新标记阶段之前强制进行一次YoungGC,通过设置CMSParallelRemarkEnabled参数可以开启并行的Remark,加快remark的速度。

并发清理

    移除那些不用的对象,回收他们占用的空间并且为将来使用。该阶段有可能产生浮动垃圾,可以通过参数UseCMSCompactAtFullCollection和CMSFullGCsBeforeCompaction来控制压缩次数。

并发重置

    该阶段是最后一个阶段,重置CMS的数据结构。

 

G1垃圾收集器

将原有的内存模型划分成了每一个区域,包含Eden区,还包含S区,还包含O区,以及H区, 其中H区存放短暂的占用空间50%以上的大对象
YoungGC回收:
1.专门回收Eden区的数据,当Eden区内存满了的情况下,会进行垃圾回收,Eden区的数据存活的对象会转移到Survivor区域,如果Survivor区域内存太小,那么Eden区就会将这个数据提升到Old区当中
2.当suvivor区域满了会将数据转移到Old区
3.Rset:记录引用地址,方便于快速定位,节省资源
在垃圾回收的时候,我们需要定位到根对象,找根对象的引用关系,之前没有G1时,我们进行内存对象的全部扫描,G1提供一个Rset
Rset专门存储引用的对象的位置,在哪一个区域,在哪一个Card当中
G1垃圾收集器会将每一块Region分为若干个Card,每一个Card默认大小为512KB

MixedGC:
当越来越多的数据晋升到Old区域当中的情况下, 为了避免内存不足的情况,JVM虚拟机会启用MiexdGC,进行混合数据的回收,包含YongGC以及部分OldGC
当老年代数据占用堆内存整体45%的时候会触发,可以通过 -XX:InitiatingHeapOccupancyPercent=n进行设置
MixedGC回收的两个部分:
1.全局标记
初始化标记
根节点扫描
全局标记
重新标记
清除垃圾:并不是真正清除,而是恢复状态
2.对象拷贝阶段
将要回收的区域的存活对象复制到另外一个Region当中,然后进行垃圾清理
G1参数:设置启用G1 设置暂停时间 设置堆内存大小
-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+PrintGCDetails -Xmx32m
优化建议:
1.不要设置年轻代内存大小
2.暂停时间不要太苛刻

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!