JVM基础之虚拟机垃圾回收详解(五)

程序所使用的内存空间不断扩大,是GC算法演进的根本原因。

一、垃圾回收器算法总览

垃圾回收器(Garbage Collection)是Java虚拟机(Java Virtual Machine),用于清理堆空间(Heap Space)中无引用对象(Non-Reference Object)的算法的实现。

1. Serial算法
  • Serial是单线程的垃圾回收算法 ,包括Serial新生代回收器、Serial Old老年代回收器。
  • 在标记阶段需要停止所有用户线程(Stop-The-World)。
2. Parallel算法
  • Parallel是多线程的垃圾回收算法,包括Parallel Scavenge新生代回收器、Parallel Old老年代回收器、ParNew新生代回收器(为了与CMS老年代回收器配合使用)。
  • 在标记阶段需要停止所有用户线程(Stop-The-World)。
3. CMS算法

CMS垃圾回收器的全称是“Concurrent-Mark-Sweep Collector”,是老年代的回收算法,在执行时分为四个阶段,包括:

  1. 初始标记(CMS-initial-mark),需要停止所有用户线程。
  2. 并发标记(CMS-concurrent-mark),与用户线程并发执行。
  3. 重新标记(CMS-remark)阶段,需要停止所有用户线程。
  4. 并发清除(CMS-concurrent-sweep),与用户线程并发执行。
  • 对老年代进行垃圾回收时,老年代的内存空间在垃圾回收完成之前耗尽,会触发“Concurrent Mode Failure”机制,导致整个虚拟机停止,并启动SerialOld垃圾回收器对老年代进行垃圾清理(标记-压缩)。
  • 为新对象在新生代中分配内存时,因对象大小符合某些标准,需要直接分配到老年代,而老年代的连续内存空间不足,会触发“Promotion Failed”机制,导致老年代进入CMS垃圾回收过程。

在标记阶段使用的算法,包括:

  • 写屏障(post-Write Barrier),在操作对象的同时,进行一些其他的操作,即拦截器机制(Interceptor)。
  • 并发标记阶段使用三色标记算法(Tri-Color Marking),图深度优先搜索的变种,未到达的节点为白色,到达过但未退回的节点为灰色,到达过且已退回的节点为黑色。
  • 增量更新(Incremental Update),在并发标记阶段,如果对象的引用关系发生变化,并且是白色节点被黑色节点引用,则将黑色节点变为灰色节点(记录到Marking Stack或者Mod-Union Table)。如果上述变化的节点再次被移出引用链,则会产生“浮动垃圾”问题。
  • CMS垃圾回收器作为老年代回收器,在重新标记阶段,HotSpot中需要从根集合(包括Stack、Register、Globals以及Young Gen)进行重新标记。
4. G1算法

G1垃圾回收器的全称是“Garbage-First Collector”,是全代的回收算法,分为Fully Young GC、Old GC、Mixed GC,可以设定回收时虚拟机的最大停顿时间 -XX:MaxGCPauseMills=250,以达到暂停时间可控的目标(Pause时间越短,GC次数越频繁,牺牲了吞吐量)。

G1回收算法将堆空间划分为若干个区域Region -XX:G1HeapRegionSize=2048,区域Region的大小由堆大小除以区域数量获得(这个值必须向下近似取整为1MB、2MB、4MB、8MB、16MB、32MB其中一个),每个区域Region具有不同的属性(包括eden、survivor、old、humongous)在逻辑上构成了Eden区、Survivor区、Tenured区、Humongous区(巨大对象区)。

G1回收算法无需一次将堆空间完全回收,它将不同的区域Region计为一个Collection Set(CSet),然后根据在标记阶段获得的关于垃圾对象的信息,优先回收垃圾对象最多的CS内的区域Region。

为了提高垃圾回收算法的效率,G1使用了Dirty Card Queue、Card Table和Remembered Set三种数据结构进行优化,但是会带来额外10%的内存空间开销。

  • Card Table是为了解决老年代对年轻代对象引用的扫描效率问题,当发生年轻代GC时,只需要扫描部分Card Table对应的堆区域即可,大大减少了扫描堆空间的范围。一个Card Table默认覆盖512字节的堆空间,当这个范围内的对象引用发生变化时,将Card Table变为Dirty,标明该范围的堆空间需要被扫描。
  • Remembered Set是为了进一步提高年轻代扫描的效率,每个区域Region对应一个RSet,在RSet中记录了引用区域Region内对象的对象的地址所在的Card Table,当发生年轻代GC时,只需要对RSet中记录的Card Table进行扫描即可,极大减少了扫描堆空间的范围。
  • 使用写屏障(Write Barriers)在RSet中记录Card Table时,为了避免并发操作的竞争开销,将需要记录的Card Table先写入到Dirty Card Queue中,根据阈值使用不同策略将Dirty Card Queue中的Card Table写入到RSet。当发生GC时,也会将Dirty Card Queue中的信息全部写入到RSet中。

Fully Young GC需要暂停虚拟机(Stop-The-World),它包括几个阶段:

  1. 构建Collection Set
  2. 扫描GC Roots
  3. 根据Dirty Card Queue更新RSet
  4. 根据RSet找到老年代对新生代对象的引用
  5. 将存活的对象复制到新的区域Region(survivor属性)
  6. 更新存活对象的引用地址

Mixed GC需要暂停虚拟机(Stop-The-World),对年轻代和部分老年代进行回收,它包括几个阶段:

  1. Fully Young GC时,复用GC Roots扫描的结果,继续进行“全局并发标记(Global Concurrent Marking)”。全局并发标记包括初始标记、并发标记、重新标记、清除空区域四个步骤,其中“清除空区域(Cleanup)”将没有存活对象的区域Region清空并加入free list中。
  2. Global Concurrent Marking结束后,根据活对象在区域Region的占比参数-XX:G1MixedGCLiveThresholdPercent=65和CSet最大数量参数-XX:G1OldCSetRegionThresholdPercent=10的设置的值,对垃圾占比更高的区域Region进行垃圾回收。
  3. Mixed GC的垃圾回收使用复制算法,与Fully Young GC相同。需要注意的是,G1为了满足用户设置的最大停顿时间,有可能将Mixed GC的清理阶段分为多次完成,并且不能超过最大次数参数-XX:G1MixedGCCountTarget=8的限制。

当一个对象占用的内存空间大于区域Region的一半,则将其直接分配到Humongous区域,如果对象超过一个区域Region的大小,则连续分配多个区域Region作为Humongous区。Humongous区不参与上述两种GC的复制过程,只有当对象被标记为垃圾后,直接清除。

在并发标记阶段,为避免错误标记垃圾对象,使用写屏障(pre-Write Barriers),当对象的引用关系发生变化,则将原有的引用关系记录,在重新标记阶段再次标记,称为SATB(Snapshot-At-The-Beginning)。因为在删除引用关系时,被删除的引用依旧会记录,所以同样会出现“浮动垃圾”问题。

5. ZGC算法
  • 读屏障
  • 着色指针算法(ColoredPointers),Java对象的指针共有64bit(ZGC不能使用压缩指针),其中44bit用于记录对象的内存地址(因此ZGC最多管理2^44=16TB的内存空间),从剩余的20bit中拿出4bit用于记录对象当前的状态。
6. Shenandoah算法
  • 写屏障
  • 着色指针算法(ColoredPointers)

二、虚拟机优化方法

  • 虚拟机优化需要设置启动程序时的参数,具体的参数见官方文档:Java Command Arguments(Java命令行参数)
  • 在Debug版本的虚拟机可以使用命令查看所有可以设置的参数:java -XX:+PrintFlagsWithComments
  • 通过命令可以查看虚拟机启动前后所有设置的参数值:java -XX:+PrintFlagsInitialjava -XX:+PrintFlagsFinal
  • 使用阿里开源的虚拟机诊断工具 - Arthas:Arthas(Alibaba Java Diagnostic Tool)
1. 根据要求对虚拟机进行预设置
2. 优化虚拟机运行时的卡顿和吞吐量
3. 解决虚拟机运行中出现的异常问题

设置GC日志的格式,确保记录的日志能够提供足够的信息用于排查问题。

三、其他问题

1. 读写屏障

写屏障即拦截器模式的前置和后置处理回调方法,R神在ITeye论坛回答问题时贴出的Hotspot代码,如下

// Write Barriers
void oop_field_store(oop* field, oop value) {  
  pre_write_barrier(field);  
  *field = value; // the actual store  
  post_write_barrier(field, value);  
} 
2. 安全点

虚拟机在进行垃圾回收时,某些阶段需要停止所有的用户线程(即Stop-The-World),而为了将线程安全地停止,虚拟机对线程可以停止的位置进行了定义,称为“安全点(SafePoint)”,包括:

  • 调用方法时
  • 方法返回时
  • 方法抛出异常时
  • 循环语句结束时
  • 安全区域(Safe Region)

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值