GC算法 - 垃圾收集器

1、内存的回收

  • 引用计数法:给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加1;当引用失效,计数器就减1;任何时候计数器为0的对象就是不可能再被使用的。(很难解决循环引用的问题)
    引用计数法

  • 可达性分析算法:将“GC Roots” 对象作为起点,从这些节点开始向下搜索引用的对象,找到的对象都标记为非垃圾对象,其余未标记的对象都是垃圾对象(根节点:线程栈的本地变量、静态变量、本地方法栈的变量等等)
    timg.jpg

  • 标记-清除法:算法分为“标记”和“清除”阶段:标记存活的对象, 统一回收所有未被标记的对象;也可以标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象 。(效率低下,内存碎片)
    标记清除

  • 标记-整理法:标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
    标记整理

  • 复制算法:将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收(浪费内存空间)
    复制

2、垃圾收集器

  • Serial收集器(-XX:+UseSerialGC-XX: +UseSerialOldGC):单线程收集器,在垃圾收集的时候只会使用一条垃圾收集线程去完成垃圾收集工作,必须停止其他所有的工作线程(STW),直到收集结束(新生代使用复制算法,老年代使用标记-整理算法)
    缺点:较长时间的STW,收集效率慢
    优点:由于没有线程交互的开销,有着非常高的单线程收集效率
    Serial

  • Parallel Scavenge收集器(-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代)):Serial的多线程版本,关注的是高吞吐量(CPU中用于运行代码的时间于CPU总消耗的时间的比值)(新生代使用复制算法,老年代使用标记-整理算法)
    Parallel Scavenge

  • ParNew收集器(-XX:+UseParNewGC):ParNew收集器其实跟Parallel收集器很类似,区别主要在于它可以和CMS收集器配合使用。(新生代使用复制算法,老年代使用标记-整理算法)
    ParNew

  • CMS收集器(-XX:+UseConcMarkSweepGC(old)):CMS收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用,它是HotSpot虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。(标记-清除)
    CMS

初始标记: 暂停所有的其他线程(STW),并记录下gc roots直接能引用的对象,速度很快。

并发标记: 并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程, 这个过程耗时较长但是不需要停顿用户线程, 可以与垃圾收集线程一起并发运行。因为用户程序继续运行,可能会有导致已经标记过的对象状态发生改变。

重新标记: 重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。主要用到三色标记里的增量更新算法(见下面详解)做重新标记。

并发清理: 开启用户线程,同时GC线程开始对未标记的区域做清扫。这个阶段如果有新增对象会被标记
为黑色不做任何处理(见下面三色标记算法详解)。

并发重置:重置本次GC过程中的标记数据

**优点:**并发收集器,低停顿
**缺点:**无法处理浮动垃圾(在并发标记和并发清理阶段产生的垃圾,只能等待下一次的GC);会产生大量大空间碎片;执行过程中的不确定性,一边回收,系统一边运行,如果此时正好油触发了一次Full GC。也就是“concurrent mode failure”,此时会进入STW,使用Serial来回收

CMS参数设置:
-XX:+UseConcMarkSweepGC:启用cms
-XX:ConcGCThreads:并发的GC线程数
-XX:+UseCMSCompactAtFullCollection:FullGC之后做压缩整理(减少碎片)
-XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩一次,默认是0,代表每次FullGC后都会压缩一次
-XX:CMSInitiatingOccupancyFraction: 当老年代使用达到该比例时会触发FullGC(默认是92,这是百分比)
-XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设
定的值),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整
-XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次minor gc,目的在于减少老年代对年轻代的引
用,降低CMS GC的标记阶段时的开销,一般CMS的GC耗时 80ss%都在标记阶段
-XX:+CMSParallellnitialMarkEnabled:表示在初始标记的时候多线程执行,缩短STW
-XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,缩短STW;

  • G1收集器(-XX:+UseG1GC):主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征
    G1
    G1将Java堆划分为很多大小相等的独立区域(Region),JVM最多可以有2048个Region。一般Region大小等于堆的大小除以2048,也可以用参数手动指定Region大小。默认年轻代对堆内存的占比是5%,也可以用参数手动指定占比,E:S:S = 8:1:1。如图所示,一个region可以是old,也可以是eden。也就是说Region的功能区域可能会动态变化。大对象的处理比较特别,引入了Humongous区,而不是让大对象直接进入老年代的Region中(当一个对象大小超过了一个Region的50%,如果太大可以使用连续的Humongous - Region存放),专门为了存放短期的大对象,节约老年的空间,避免频繁的Full GC,Full GC除了回收年轻代和老年代,Humongous也会一起回收。
    G1

*初始标记:*同CMS

*并发标记:*同CMS

*最终标记:*同CMS重新标记

*筛选回收:*筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来定制回收,这个阶段其实也可以做到与用户程序一起并发执行,但是很大概率上只能回收一部分的Region,时间是用户可以控制的,停顿用户线程收集效率会大幅度的提高不管是年轻代或者是老年代,使用的都是复制算法,不会有很多的内存碎片。G1收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值大的Region(Garbage - First)

*Young GC:*并不是Eden区放满了就马上触发,G1通过计算得到当前回收的时间和用户设置的回收时间接近时才会触发
*Mixed GC:*当老年代的占有率达到设定的值则触发,回收Young和Old以及大对象,正常情况下G1的垃圾收集先做Mixed GC,主要使用复制算法,拷贝过程中如果发现没有足够的内存空间,则触发Full GC
*Full GC:*停止系统程序,采用单线程来进行标记,清理和压缩整理,空闲出来一批Region来提供下一次Mixed GC使用,非常耗时

G1收集器参数设置
-XX:+UseG1GC:使用G1收集器
-XX:ParallelGCThreads:指定GC工作的线程数量
-XX:G1HeapRegionSize:指定分区大小(1MB~32MB,且必须是2的N次幂),默认将整堆划分为2048个分区
-XX:MaxGCPauseMillis:目标暂停时间(默认200ms)
-XX:G1NewSizePercent:新生代内存初始空间(默认整堆5%)
-XX:G1MaxNewSizePercent:新生代内存最大空间
-XX:TargetSurvivorRatio:Survivor区的填充容量(默认50%),Survivor区域里的一批对象(年龄1+年龄2+年龄n的多个年龄对象)总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代
-XX:MaxTenuringThreshold:最大年龄阈值(默认15)
-XX:InitiatingHeapOccupancyPercent:老年代占用空间达到整堆内存阈值(默认45%),则执行新生代和老年代的混合收集(MixedGC),比如我们之前说的堆默认有2048个region,如果有接近1000个region都是老年代的region,则可能就要触发MixedGC了
-XX:G1MixedGCLiveThresholdPercent(默认85%): region中的存活对象低于这个值时才会回收该region,如果超过这个值,存活对象过多,回收的的意义不大。
-XX:G1MixedGCCountTarget:在一次回收过程中指定做几次筛选回收(默认8次),在最后一个筛选回收阶段可以回收一会,然后暂停回收,恢复系统运行,一会再开始回收,这样可以让系统不至于单次停顿时间过长。
-XX:G1HeapWastePercent(默认5%): gc过程中空出来的region是否充足阈值,在混合回收的时候,对Region回收都是基于复制算法进行的,都是把要回收的Region里的存活对象放入其他Region,然后这个Region中的垃圾对象全部清理掉,这样的话在回收过程就会不断空出来新的Region,一旦空闲出来的Region数量达到了堆内存的5%,此时就会立即停止混合回收,意味着本次混合回收就结束了。

  • 三色标记:
    三色标记

黑色: 表示对象已经被垃圾收集器访问过, 且这个对象的所有引用都已经扫描过。 黑色的对象代表已经扫描过, 它是安全存活的, 如果有其他对象引用指向了黑色对象, 无须重新扫描一遍。 黑色对象不可能直接(不经过灰色对象) 指向某个白色对象

灰色: 表示对象已经被垃圾收集器访问过, 但这个对象上至少存在一个引用还没有被扫描过

白色: 表示对象尚未被垃圾收集器访问过。 显然在可达性分析刚刚开始的阶段, 所有的对象都是白色的, 若在分析结束的阶段, 仍然是白色的对象, 即代表不可达

  • 漏标-读写屏障:
    漏标会导致被引用的对象被当成垃圾误删除,有两种解决方案: 增量更新(IncrementalUpdate) 和原始快照(Snapshot At The Beginning,SATB)

*增量更新:*当黑色对象插入新的指向白色对象的引用关系时, 就将这个新插入的引用记录下来, 等并发扫描结束之后, 再将这些记录过的引用关系中的黑色对象为根, 重新扫描一次。 这可以简化理解为, 黑色对象一旦新插入了指向白色对象的引用之后, 它就变回灰色对象了。

*原始快照:*当灰色对象要删除指向白色对象的引用关系时, 就将这个要删除的引用记录下来, 在并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根, 重新扫描一次,这样就能扫描到白色的对象,将白色对象直接标记为黑色(目的就是让这种对象在本轮gc清理中能存活下来,待下一轮gc的时候重新扫描,这个对象也有可能是浮动垃圾)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值