IDEA2023.1.3破解,IDEA破解,IDEA 2023.1破解,最新IDEA激活码

JVM–浅谈G1收集器

IDEA2023.1.3破解,IDEA破解,IDEA 2023.1破解,最新IDEA激活码

本篇文章内容转载至:知乎-G1 收集器原理理解与分析-林林,侵删~

1 引言

G1 收集器应该是当前 Java GC 收集器中最新流行的代表作,刚好前段时间也处理了一个 Full GC 之后老年代使用率仍然居高不下的 case,使用的就是 G1 收集器,遂决定简单学习一下 G1 收集器的原理并加以记录。

2 简介

G1 收集器是一款在 Server 端运行的垃圾收集器,专门针对于拥有多核处理器大内存的机器,在 JDK 7u4 版本发行时被正式推出,在 JDK9 中更被指定为官方 GC 收集器。它满足高吞吐量的同时又满足 GC 停顿的时间尽可能短。G1 收集器专门针对以下应用场景设计:

  • 可以像 CMS 收集器一样和应用并发运行
  • 压缩空闲的内存碎片,却不需要冗长的 GC 停顿
  • 对 GC 停顿可以做更好的预测
  • 不想牺牲大量的吞吐量性能
  • 不需要更大的 Java Heap

G1 从长期计划来看是以取代 CMS 为目标的。与 CMS 相比有几个不同点使得 G1 成为 GC 更好的解决方案。第一点:G1 会压缩空闲内存使之足够紧凑,做法是用 regions 代替细粒度的空闲列表进行分配,减少内存碎片的产生。第二点:G1 的 STW(stop the world, 应用停顿)更可控,G1 在停顿时间上添加了预测机制,用户可以指定期望停顿时间。

3 详述

3.1 堆内存划分

在传统的 GC 收集器(serial, parallel, CMS) 中无一不例外都把 heap 分成固定大小且连续的三个空间:young generation,old generation,以及 permanent generation。

85_1.png

但 G1 却独辟蹊径,采用了一种全新的内存布局。

85_2.png

在 G1 中堆被分成一块块大小相等的 heap region,一般有 2000 多块,这些 region 在逻辑上是连续的。每块 region 都会被打上唯一的分代标志(eden, survivor, old)。在逻辑上,eden regions 构成 Eden 空间,survivor regions 构成 Survivor 空间,old regions 构成了Old 空间。

可以通过命令行参数 -XX:NewRatio=n 来配置新生代与老年代的比例,默认为 2,即比例为 2:1;-XX:SurvivorRatio=n 则可以配置 Eden 与 Survivor 的比例,默认为 8。

3.2 Region简介

G1 中每个 Region 大小是固定相等的,Region 的大小可以通过参数 -XX:G1HeapRegionSize 设定,取值范围从 1M 到 32M,且是 2 的指数。如果不设定,那么 G1 会根据 Heap 大小自动决定。

决定逻辑:

size =(堆最小值+堆最大值)/ TARGET_REGION_NUMBER(2048) ,然后 size 取最靠近 2 的幂次数值,并将 size 控制在 [1M, 32M] 之间。

3.3 GC过程

G1 保留了 YGC 并加上了一种全新的 MIXGC 用于收集老年代。G1 只有在特定情况下才会触发 Full GC,G1 的 Full GC 采用的是 serial old Full GC。

在了解 G1 收集器的 GC 过程前,先来明确一些概念。

3.3.1 STAB

STAB,全称为 snapshot-at-the-beginning,其目的是为了维持并发 GC 的正确性。GC 的正确性是保证存活的对象不被回收,回收的都是垃圾。如果标记过程是 STW 的话,那 GC 的正确性是一定能保证的。但如果一边标记,一边应用在变更堆里面对象的引用,那么标记的正确性就不一定能保证了。

为了解决这个问题,STAB 的做法是在 GC 开始时对内存进行一个对象图的逻辑快照(snapshot),只要被快照到的对象是活的,那在整个 GC 的过程中对象就被认定的是活的,即使该对象的引用稍后被修改或者删除。同时新分配的对象也会被认为是活的,除此之外其它不可达的对象就被认为是死掉了。这样 STAB 就保证了真正存活的对象不会被 GC 误回收,但同时也造成了某些可以被回收的对象逃过了 GC,导致了内存里面存在浮动的垃圾(float garbage)。

3.3.2 global concurrent marking

GC 时 G1 的运行方式与 CMS 方式类似,会有一个全局并发标记(global concurrent marking)的过程,去确定堆里各个 Region 对象的存活情况。

全局并发标记过程分为五个阶段:

  • Initial Mark(初始标记,STW)
  • Root Region Scanning(根区域扫描)
  • Concurrent Marking(并发标记)
  • Remark(最终标记,STW)
  • Cleanup(清除,STW AND Concurrent)

这五个阶段的过程及实现很复杂,博主也没有完全搞明白,因此就不再做详细介绍了。有兴趣的可以阅读这篇文章:G1 收集器原理理解与分析

经过 global concurrent marking,G1 就知道哪些 Region 有存活的对象,并将那些完全可回收的 Region(没有存活对象)收集起来加入到可分配的 Region 队列中,实现对该部分内存的回收。对于有存活对象的 Region,G1 会根据统计模型找处收益最高、开销不超过用户指定上限的若干 Region 进行对象回收。这些被选中回收的 Region 组成的集合就叫做 collection set,简称 Cset。

3.3.3 YGC

当 Eden 空间被占满之后,就会触发 YGC。在 G1 中 YGC 依然采用复制存活对象到 Survivor 空间的方式,当对象的存活年龄满足晋升条件时,就会把对象提升到 old generation regions(老年代)。

G1 控制 YGC 开销的手段是动态改变 young region 的个数,YGC 的过程中依然会 STW,并采用多线程并发复制对象,减少 GC 停顿时间。

YGC 之前:

85_3.png

YGC 之后:

85_4.png

这两张图也说明了 G1 在进行 YGC 的时候的确会压缩空闲的内存碎片。

3.3.4 MIXGC

G1 中的 MIXGC 选定所有新生代里的 Region,外加根据 global concurrent marking(全局并发标记)统计得出收集收益高的若干老年代 Region,在用户指定的开销目标范围内尽可能选择收益高的老年代 Region 进行回收。所以 MIXGC 回收的内存区域是新生代 + 老年代。

3.3.5 FGC

G1 的正常处理流程中没有 Full GC,只有在垃圾回收处理不过来(或者主动触发)时才会出现。G1 的 Full GC 就是单线程执行的 Serial Old GC,会导致非常长的 STW,是调优的重点,需要尽量避免 Full GC。常见导致 G1 Full GC 的原因如下:

  • 程序主动执行 System.gc()
  • 全局并发标记期间老年代空间被填满(并发模式失败)
  • MIXGC 期间老年代空间被填满(晋升失败)
  • Young GC 时 Survivor 空间和老年代没有足够空间容纳存活对象

3.3.6 小结

1、 MIXGC 中的 Cset 是选定所有 young gen 里的 region,外加根据 global concurrent marking 统计得出收集收益高的若干old gen region;
2、 YGC 中的 Cset 是选定所有 young gen 里的 region。通过控制 young gen 的 region 个数来控制 young GC 的开销;
3、 YGC 与 MIXGC 都是采用多线程复制清除,整个过程会 STW。G1 低延迟的原理在于其回收的区域变得精确并且范围变小了;
4、 G1 将其收集和压缩活动集中在堆中可能充满可回收对象的区域,使用暂停预测模型来满足用户定义的暂停时间目标,并根据指定的暂停时间目标选择要收集的区域数量。

在第四点中需要注意到的是,它能够以较高的概率满足设定的暂停时间目标,但并不是绝对确定的。G1 根据以前收集的数据,估算出在用户指定的目标时间内可以收集多少个区域。因此,收集器对于收集区域的成本会有一个相当准确的模型,它使用这个模型来确定在暂停时间目标内收集哪些区域和收集区域内多少可回收对象会比较合适。

4 G1 命令行选项与最佳实践

4.1 命令行选项

  • -XX:+UseG1GC:告诉 JVM 使用 G1 收集器
  • -XX:MaxGCPauseMillis=200:设置 GC 最大目标停顿时间为 200ms,这是一个软指标,JVM 会尽最大努力去达到它。G1 默认配置就是 200ms

4.2 最佳实践

1、不要设置 Young Generation 大小

显式通过 -Xmn 设置 young generation 的大小将会干预 G1 收集器的默认行为。

  • G1 将不再尊重设定的目标停顿时间,本质来说是因为设置 young generation 的大小使得设定的目标停顿时间失效
  • 由于大小是固定的,所以 G1 不再能够根据需要扩展和收缩 young generation 的空间

2、Evacuation Failure

当 JVM 在 GC 期间复制对象到 Survior 区或者提升对象到老年代时,如果堆空间被耗尽,堆无法扩展,而导致复制对象或升级失败,这种情况下 -XX:+PrintGCDetails 将会以空间溢出(to-space overflow)的形式表示,其代价是十分昂贵的:

  • GC 仍然需要继续,所以必须释放空间
  • 未成功复制的对象必须在适当的位置保留

因此,为了避免 Evacuation Failure,可以考虑增大 G1 预留内存的大小,G1 会预留一部分内存,制造一个假的天花板,当真正 Evacuation Failure 时还有内存可用。设置预留内存占比百分比的参数是 -XX:G1ReservePercent=n,其默认值是 10。

3、巨型对象分配

巨型对象:对象大小大于等于 Region 的一半。当 Region 中包含一个巨型对象时,其剩余的空间将不再被利用,从而导致空间碎片化。当 G1 没有合适空间分配巨型对象时,G1 会启动串行 Full GC 来释放空间。可以通过设置 -XX:G1HeapRegionSize 来增大 Region 大小,这样一来,相当一部分的巨型对象就不再是巨型对象了,而是采用普通的分配方式。

5 附录

5.1 G1相关参数

  • -XX:+UseG1GC:使用 G1 GC
  • -XX:MaxGCPauseMillis=n:设置最大 GC 停顿时间,这是一个软目标,JVM 会尽最大努力去达到它
  • -XX:NewRatio=n:新生代和老年代大小的比例,默认是 2
  • -XX:SurvivorRatio=n:Eden 和 Survivor 区域空间大小的比例,默认是 8
  • -XX:MaxTenuringThreshold=n:晋升的阈值,默认是 15(一个存活对象经历多少次 GC 周期之后晋升到老年代)
  • -XX:ParallelGCThreads=n:设置 GC 并发阶段的线程数,默认值与 JVM 运行平台相关
  • -XX:ConcGCThreads=n:设置并发标记的线程数,默认值与 JVM 运行平台相关
  • -XX:G1ReservePercent=n:设置预留内存所占 Java 堆内存大小比例,用于防止晋升失败/Evacuation Failure,默认值是 10%
  • -XX:G1HeapRegionSize=n:设置 Region 的大小,默认是根据堆的大小动态决定,大小范围是[1M,32M]

5.2 GC Roots算法搜寻根节点枚举为什么会STW?

迄今为止,所有的收集器在根节点枚举这一步骤时都必须暂停用户线程。GC Roots 可达性算法查找引用链这个过程是可以做到和用户线程一起并发的,但根节点枚举始终必须保持在某个快照中才能保证一致性。快照一致性是指在某个时间节点,系统内的所有对象都像是被冻结在某个时间点上,不会出现分析的过程中根节点和对象的引用关系还在不断变化的情况。这也是垃圾收集过程中必须停顿用户线程的一个重要原因,即使是号称停顿时间可控的 CMS、G1、ZGC 等收集器,在枚举根节点的时候也必须是要停顿的!

6 参考阅读

1、 G1 收集器原理理解与分析
2、 Java Hotspot G1 GC的一些关键技术
3、 深入理解G1垃圾收集器及日志分析

文章永久链接:https://tech.souyunku.com/?p=40895


Warning: A non-numeric value encountered in /data/wangzhan/tech.souyunku.com.wp/wp-content/themes/dux/functions-theme.php on line 1154
赞(96) 打赏



未经允许不得转载:搜云库技术团队 » JVM–浅谈G1收集器

IDEA2023.1.3破解,IDEA破解,IDEA 2023.1破解,最新IDEA激活码
IDEA2023.1.3破解,IDEA破解,IDEA 2023.1破解,最新IDEA激活码

评论 抢沙发

大前端WP主题 更专业 更方便

联系我们联系我们

觉得文章有用就打赏一下文章作者

微信扫一扫打赏

微信扫一扫打赏


Fatal error: Uncaught Exception: Cache directory not writable. Comet Cache needs this directory please: `/data/wangzhan/tech.souyunku.com.wp/wp-content/cache/comet-cache/cache/https/tech-souyunku-com/index.q`. Set permissions to `755` or higher; `777` might be needed in some cases. in /data/wangzhan/tech.souyunku.com.wp/wp-content/plugins/comet-cache/src/includes/traits/Ac/ObUtils.php:367 Stack trace: #0 [internal function]: WebSharks\CometCache\Classes\AdvancedCache->outputBufferCallbackHandler() #1 /data/wangzhan/tech.souyunku.com.wp/wp-includes/functions.php(5109): ob_end_flush() #2 /data/wangzhan/tech.souyunku.com.wp/wp-includes/class-wp-hook.php(303): wp_ob_end_flush_all() #3 /data/wangzhan/tech.souyunku.com.wp/wp-includes/class-wp-hook.php(327): WP_Hook->apply_filters() #4 /data/wangzhan/tech.souyunku.com.wp/wp-includes/plugin.php(470): WP_Hook->do_action() #5 /data/wangzhan/tech.souyunku.com.wp/wp-includes/load.php(1097): do_action() #6 [internal function]: shutdown_action_hook() #7 {main} thrown in /data/wangzhan/tech.souyunku.com.wp/wp-content/plugins/comet-cache/src/includes/traits/Ac/ObUtils.php on line 367