JVM(五)G1垃圾收集器详解

一、G1垃圾收集器简介

  为什么单独写一篇文章来记录G1垃圾收集器的学习过程呢?因为上一篇文章主要都是针对8G内存以下的服务器来进行总结的,G1的特点主要是针对大内存的机器,讲道理一般的公司也基本上用不到那么大的内存,所以这篇文章先单独记录一下吧~

  简介:G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多核处理器及大容量内存的机器

  特点:STW停顿时间敏感,提升用户体验,高吞吐量,CPU利用率高。

  从上图看,G1将Java堆划分为多个大小相等的独立区域(Region),每一个小方格代表一个Region,JVM最多可以有2048个Region。

  一般Region大小等于堆大小除以2048,比如堆大小为4096M,则Region大小为2M,当然也可以用参数-XX:G1HeapRegionSize手动指定Region大小,但是推荐默认的计算方式

  G1保留了年轻代和老年代的概念,但不再是物理隔阂了,它们都是(可以不连续)Region的集合

  一个Region可能之前是年轻代,如果Region进行了垃圾回收,之后可能又会变成老年代,也就是说Region的区域功能可能会动态变化

  默认年轻代对堆内存的占比是5%,在系统运行中,JVM会不停的给年轻代增加更多的Region,但是最多新生代的占比不会超过60%。

  PS:年轻代中的Eden和Survivor对应的region也跟之前一样,默认8:1:1

Humongous区

  G1垃圾收集器对于对象什么时候会转移到老年代跟之前讲过的原则一样,唯一不同的是对大对象的处理,G1有专门分配大对象的Region叫Humongous区,而不是让大对象直接进入老年代的Region中。在G1中,大对象的判定规则就是一个大对象超过了一个Region大小的50%,比如每个Region是2M,只要一个对象超过了1M,就会被放入Humongous中,而且一个大对象如果太大,可能会横跨多个Region来存放。

  作用:Humongous区专门存放短期巨型对象,不用直接进老年代,可以节约老年代的空间,避免因为老年代空间不够的GC开销

  PS:Full GC的时候除了收集年轻代和老年代之外,也会将Humongous区一并回收。

二、G1垃圾收集器GC步骤

初始标记【STW】

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

并发标记

  并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象集合的过程, 这个过程耗时较长但是不需要停顿用户线程, 可以与垃圾收集线程一起并发运行。(同CMS)

重新标记/最终标记【STW】

  重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。(同CMS)

筛选回收【STW】

  定义:筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间(可以用JVM参数 -XX:MaxGCPauseMillis指定)来制定回收计划

  举个🌰:比如说老年代此时有1000个Region都满了,但是因为根据预期停顿时间,本次垃圾回收可能只能停顿200毫秒,通过回收成本计算得知,可能回收其中800个Region刚好需要200ms,那么就只会回收800个Region,尽量把GC导致的停顿时间控制在我们指定的范围内。

  这个阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅提高收集效率。

  回收算法:不管是年轻代或是老年代,回收算法主要用的是复制算法,将一个region中的存活对象复制到另一个region中。

  PS:这种不会像CMS那样回收完因为有很多内存碎片还需要整理一次,G1采用复制算法回收几乎不会有太多内存碎片。

  PS:CMS回收阶段是跟用户线程一起并发执行的,G1因为内部实现太复杂暂时没实现并发回收,不过到了Shenandoah就实现了并发收集,Shenandoah可以看成是G1的升级版本

  筛选回收如何实现?:G1收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的Region。比如一个Region花200ms能回收10M垃圾,另外一个Region花50ms能回收20M垃圾,在回收时间有限情况下,G1当然会优先选择后面这个Region回收。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器在有限时间内可以尽可能高的收集效率。

并发重置

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

三、G1垃圾收集器的特点

并行与并发

  G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短STW停顿时间。部分其他收集器原本需要停顿Java线程来执行GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行。

分代收集

  虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但是还是保留了分代的概念。

空间整合

  与CMS的标记-清理算法不同,G1从整体来看是基于标记-整理算法实现的收集器,从局部上来看是基于标记-复制算法实现的。

可预测的停顿

  这是G1相对于CMS的另一个大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型【后台维护的优先列表】,能让使用者明确指定在一个长度为M毫秒的时间片段(通过参数-XX:MaxGCPauseMillis指定)内完成垃圾收集。

小结

  毫无疑问, 可以由用户指定期望的停顿时间是G1收集器很强大的一个功能, 设置不同的期望停顿时间, 可使得G1在不同应用场景中取得关注吞吐量和关注延迟之间的最佳平衡。 默认的停顿目标为两百毫秒,一般不需要调整,即使调整也应尽可能使其合理,不能太短,如果我们把停顿时间调得非常低,譬如设置为二十毫秒, 很可能每次只能回收很小的一部分内存, 收集器收集的速度逐渐跟不上分配器分配的速度, 导致垃圾慢慢堆积。 很可能一开始收集器还能从空闲的堆内存中获得一些喘息的时间, 但应用运行时间一长就不行了,最终占满堆引发Full GC反而降低性能, 所以通常把期望停顿时间设置为一两百毫秒或者两三百毫秒会是比较合理的。

四、G1垃圾收集分类

YoungGC

  YoungGC并不是说现有的Eden区放满了就会马上触发,G1会计算下现在Eden区回收大概要多久时间,如果回收时间远远小于参数 -XX:MaxGCPauseMills 设定的值,那么增加年轻代的region,继续给新对象存放,不会马上做Young GC,直到下一次Eden区放满,G1计算回收时间接近参数 -XX:MaxGCPauseMills 设定的值,那么就会触发Young GC。

  PS:所以G1垃圾收集器刚开始年轻代只占堆内存百分之5,会随着每次计算回收时间而增加,最多不超过百分之60。

MixedGC【混合收集】

  不是FullGC,老年代的堆占有率达到参数-XX:InitiatingHeapOccupancyPercent设定的值则触发,回收所有的年轻代和部分老年代(根据筛选回收阶段计算优先级后排序)以及大对象区,正常情况G1的垃圾收集是先做MixedGC,主要使用复制算法,需要把各个region中存活的对象拷贝到别的region里去,拷贝过程中如果发现没有足够的空region能够承载拷贝对象就会触发一次Full GC。

Full GC

  停止系统程序,然后采用单线程进行标记、清理、压缩整理以空闲出来一批Region来供下一次MixedGC使用,这个过程是非常耗时的。(Shenandoah优化成多线程收集了)

五、什么场景适合使用G1

50%以上的堆被存活对象占用

  使用G1,就不用特意预留出很大的老年代空间,G1会根据对象存活状态,动态分配每种不同代对象需要占用的空间。

对象分配和晋升的速度变化非常大

  前提还是大内存机器才使用G1,大内存的主机如果对象分配和晋升的速度变化非常快的话,G1的这种内存设计可以很快的划分出对应所需的区域【区域占比动态增长,不像CMS等垃圾收集器要划分固定的空间来区分年轻代和老年代】,但因为G1算法比较复杂,在小内存机器里面性能不如CMS等主流垃圾收集器。

垃圾回收时间特别长,超过1秒

  G1有一大好处就是可以设置我们每次想要回收的停顿时间【-XX:MaxGCPauseMillis】,可以有效提升用户体验。

8GB以上的堆内存(建议值)

  G1适合8G以上内存的机器使用【结构设计,2048个Region,内存太小的话每个Region也很小,很容易就超过Region的一半被识别为超大对象,这样Humongous区东西会很多,反而不能很好的进行GC收集】

停顿时间是500ms以内

  和第三点类似,G1分段收集【每次不一定全部回收完所有的垃圾对象】,并且可以手动设置我们每次GC想要STW的时间。

posted @   有梦想的肥宅  阅读(4475)  评论(2编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示