在 G1 中将 G1HeapWastePercent 设置为零的后果

Posted

技术标签:

【中文标题】在 G1 中将 G1HeapWastePercent 设置为零的后果【英文标题】:Consequences of setting G1HeapWastePercent to zero in G1 【发布时间】:2022-01-23 08:02:58 【问题描述】:

美好的一天,我在生产中使用 G1(Hotspot JDK 11)并试图了解它是如何工作的。据我了解,G1中的Old GC只清理那些充满垃圾的区域,否则区域会被放入一些队列中。

稍后会触发混合 GC,它将使用 amount of Old generations/G1MixedGCCountTarget 旧区域清理年轻区域。要扫描的旧区域由G1MixedGCLiveThresholdPercent 确定,默认情况下为 65%,即如果该区域的 65% 是垃圾,那么它将被清除。

但是我不确定我是否理解G1HeapWastePercent 的含义。 来自官方文档

设置您愿意浪费的堆百分比。当 >reclaimable 百分比小于堆浪费百分比时,Java >HotSpot VM 不会启动混合垃圾回收周期。默认值为 10%。此设置在 Java HotSpot VM 版本 23 中不可用。

假设区域包含 65% 的垃圾。 G1 开始将生命对象从年轻区域移动到一个充满垃圾的旧区域,一旦这个区域只包含 10% 的垃圾,然后 G1 切换到另一个旧区域区域。如果我的假设是正确的,那么将G1HeapWastePercent 设置为 0 将用生命物体完全填充这个区域。这是正确的吗 ?如果时间延迟不是我的应用程序的优先事项,我将 G1HeapWastePercent 设置为零有什么好处吗?

【问题讨论】:

【参考方案1】:

首先,我想你可能误解了G1MixedGCLiveThresholdPercent的效果(在Jdk7中默认值为65%,在Jdk 11中85%默认值)。 G1MixedGCLiveThresholdPercent 不是垃圾对象的空间比例,而是活对象的空间比例。如果某个区域的活体空间超过 85%(在 jdk11 中),则该区域将不会被选入 collection set(区域集合待回收)

其次,G1HeapWastePercent(Jdk7默认为10%,Jdk11默认为5%) 是触发 Mixed GC 和退出 Mixed GC 的条件。 当collection set中的垃圾对象占总堆分配空间的5%以上(在jdk11中)时, Mixed GC会被触发,而退出Mixed GC的充分条件之一就是 collection set中的垃圾对象率低于5%。

因此,如果G1HeapWastePercent设置为0%,只要收集集中有垃圾对象,就会触发Mixed GC。 那么退出混合GC的充分条件将变为collection set为空, 因为每次 Mixed GC 都会从 collection set 中移除回收的区域。

最后想给大家解释一下G1 GC的执行过程,让大家更好的理解整个过程。


G1 包含 3 个阶段,concurrent markingOnly Young GCMixed GC。大致流程如下:

    Only Young GC结束,判断是否需要启动concurrent marking concurrent marking完成后,将计算每个区域(老年代)的活动对象和垃圾对象占用的空间。
      然后生成collection set(要回收的old region collection),只有存活对象大小占region总大小超过G1MixedGCLiveThresholdPercent的old generation region才会进入collection set。 之后对collection set进行排序,_gc_efficiency最高的排名第一。
    如果collection set 中的reclaimable_percent 超过G1HeapWastePercent %,则稍后会触发Mixed GCMixed GC 将选择所有年轻区域,old_region_length 的旧区域被回收。
      Mixed GC执行结束时,如果reclaimable_percent仍然超过G1HeapWastePercent%,那么稍后会再次执行Mixed GC

让我解释一下下面提到的几个参数和概念:

G1MixedGCLiveThresholdPercent

    openjdk11中默认值为85,G1MixedGCLiveThresholdPercent 相关使用位置为mixed_gc_live_threshold_bytes,用于计算单个区域内物体的存活率阈值。超过此阈值的区域将不会进入collection set

_gc_efficiency

    单个区域的回收价值 _gc_efficiency = the size of garbage objects in a single region / the time it takes to transfer this region 如果区域内的垃圾对象越多,传输时间越短,_gc_efficiency 就会越高。

G1HeapWastePercent

    openjdk11中默认值为5,G1HeapWastePercent 相关代码next_gc_should_be_mixed用于判断下一次GC是否为Mixed GC

reclaimable_percent

    reclaimable_percent = garbage object size / total space size 这里的垃圾对象是指collection set中垃圾对象的总大小

G1MixedGCCountTarget

    openjdk11中默认值为8,G1MixedGCCountTarget 表示一旦触发 Mixed GC,Expect 会执行 8 次 Mixed GC。

G1OldCSetRegionThresholdPercent

    openjdk11中默认值为10,G1OldCSetRegionThresholdPercent 每次Mixed GC,选择的old_region_length都有一个最大值 最大值 = the number of allocated regions in heap * 10 / 100

old_region_length

    这个值表示这个Mixed GC选择collecion set中Old Regions的old_region_length进行回收 这个值的下限是min_old_cset_length,上限是max_old_cset_length min_old_cset_length = the size of collection set / G1MixedGCCountTargetmax_old_cset_length = the number of allocated regions in heap * G1OldCSetRegionThresholdPercent / 100 因为G1会严格控制每一个GC Pause Time,所以最终old_region_length受到gc暂停时间的约束。但是混合 GC 会尽可能多地将 collecion set 中的区域放入 old_region_length,直到回收这些区域所需的总时间接近估计的 GC Pause Time

【讨论】:

这真的很有帮助,非常感谢,你甚至提供了 jdk 源代码的链接,非常棒,现在我知道实际发生了什么 我认为你对 G1MixedGCCountTarget 的描述不正确,数字 8 表示 MixedGc 的目标数量而不是选择多少个旧区域,旧区域的数量将由上限确定(G1OldCSetRegionThresholdPercent ) 或当可回收空间小于 G1HeapWastePercent 时。我说的对吗? 是的,你的意见是正确的。感谢您的提醒,我更正了答案中 G1MixedGCCountTarget 的解释。

以上是关于在 G1 中将 G1HeapWastePercent 设置为零的后果的主要内容,如果未能解决你的问题,请参考以下文章

在 PHP 中将时间戳转换为时间前,例如 1 天前、2 天前……

在字符串中将'='的所有实例替换为'=='

JDK 11中将会加入令人惊叹的ZGC(不到2毫秒)

最全面的JVM G1学习笔记

G1垃圾回收器在并发场景调优

从G1设计到堆空间调整