【问题标题】:Why doesn't G1 start a marking cycle when the InitiatingHeapOccupancyPercent is achieved?为什么在达到 InitiatingHeapOccupancyPercent 时 G1 不开始标记周期?
【发布时间】:2016-08-13 20:39:53
【问题描述】:

根据documentationXX:InitiatingHeapOccupancyPercent

设置触发标记周期的 Java 堆占用阈值。 默认占用率为整个 Java 堆的 45%。

在我目前的环境中,这不会发生。

我的G1垃圾回收配置如下

-Xms25000m
-Xmx25000m
-XX:+UseG1GC
-XX:MaxGCPauseMillis=1000
-XX:GCTimeRatio=99
-XX:InitiatingHeapOccupancyPercent=70
-XX:MaxTenuringThreshold=8
-XX:+UnlockExperimentalVMOptions
-XX:G1MixedGCCountTarget=16
-XX:G1OldCSetRegionThresholdPercent=3
-XX:G1NewSizePercent=30
-XX:G1RSetUpdatingPauseTimePercent=5

对于 25g 的堆和 70% 的 XX:InitiatingHeapOccupancyPercent,您会期望在 18g 被占用时开始一个标记周期。我正在跟踪垃圾收集日志,但这并没有发生。

摘录如下:

{Heap before GC invocations=592 (full 0):
 garbage-first heap   total 25600000K, used 22802164K [0x00000001a5800000, 0x00000001a60061a8, 0x00000007c0000000)
  region size 8192K, 1526 young (12500992K), 25 survivors (204800K)
 Metaspace       used 37386K, capacity 37948K, committed 38144K, reserved 1083392K
  class space    used 3948K, capacity 4080K, committed 4096K, reserved 1048576K
2016-04-20T22:06:38.272+0000: 4213.406: [GC pause (GCLocker Initiated GC) (young)
Desired survivor size 801112064 bytes, new threshold 8 (max 8)
- age   1:   98537800 bytes,   98537800 total
- age   2:    7053912 bytes,  105591712 total
- age   3:    6556320 bytes,  112148032 total
- age   4:    8836064 bytes,  120984096 total
- age   5:    5725448 bytes,  126709544 total
- age   6:    6702728 bytes,  133412272 total
- age   7:    3831920 bytes,  137244192 total
- age   8:    4166336 bytes,  141410528 total
 4213.406: [G1Ergonomics (CSet Construction) start choosing CSet, _pending_cards: 184844, predicted base time: 44.67 ms, remaining time: 955.33 ms, target pause time: 1000.00 ms]
 4213.406: [G1Ergonomics (CSet Construction) add young regions to CSet, eden: 1501 regions, survivors: 25 regions, predicted young region time: 21.21 ms]
 4213.406: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 1501 regions, survivors: 25 regions, old: 0 regions, predicted pause time: 65.88 ms, target pause time: 1000.00 ms]
 4213.475: [G1Ergonomics (Heap Sizing) attempt heap expansion, reason: recent GC overhead higher than threshold after GC, recent GC overhead: 1.40 %, threshold: 1.00 %, uncommitted: 0 bytes, calculated expansion amount: 0 bytes (20.00 %)]
, 0.0687163 secs]
   [Parallel Time: 61.7 ms, GC Workers: 28]
      [GC Worker Start (ms): Min: 4213406.9, Avg: 4213407.1, Max: 4213407.3, Diff: 0.4]
      [Ext Root Scanning (ms): Min: 6.0, Avg: 6.2, Max: 6.4, Diff: 0.4, Sum: 173.1]
      [Update RS (ms): Min: 33.5, Avg: 34.0, Max: 34.6, Diff: 1.1, Sum: 951.9]
         [Processed Buffers: Min: 27, Avg: 36.6, Max: 48, Diff: 21, Sum: 1024]
      [Scan RS (ms): Min: 0.1, Avg: 0.2, Max: 0.5, Diff: 0.4, Sum: 6.3]
      [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
      [Object Copy (ms): Min: 20.1, Avg: 20.6, Max: 20.8, Diff: 0.7, Sum: 577.5]
      [Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.7]
         [Termination Attempts: Min: 1, Avg: 13.2, Max: 19, Diff: 18, Sum: 371]
      [GC Worker Other (ms): Min: 0.0, Avg: 0.2, Max: 0.4, Diff: 0.3, Sum: 4.7]
      [GC Worker Total (ms): Min: 60.9, Avg: 61.2, Max: 61.6, Diff: 0.6, Sum: 1714.2]
      [GC Worker End (ms): Min: 4213468.2, Avg: 4213468.3, Max: 4213468.5, Diff: 0.3]
   [Code Root Fixup: 0.4 ms]
   [Code Root Purge: 0.0 ms]
   [Clear CT: 1.2 ms]
   [Other: 5.4 ms]
      [Choose CSet: 0.0 ms]
      [Ref Proc: 0.5 ms]
      [Ref Enq: 0.0 ms]
      [Redirty Cards: 0.8 ms]
      [Humongous Register: 0.2 ms]
      [Humongous Reclaim: 0.0 ms]
      [Free CSet: 2.4 ms]
   [Eden: 11.7G(11.7G)->0.0B(11.7G) Survivors: 200.0M->200.0M Heap: 21.7G(24.4G)->10.0G(24.4G)]
Heap after GC invocations=593 (full 0):
 garbage-first heap   total 25600000K, used 10516798K [0x00000001a5800000, 0x00000001a60061a8, 0x00000007c0000000)
  region size 8192K, 25 young (204800K), 25 survivors (204800K)
 Metaspace       used 37386K, capacity 37948K, committed 38144K, reserved 1083392K
  class space    used 3948K, capacity 4080K, committed 4096K, reserved 1048576K
}
 [Times: user=1.70 sys=0.01, real=0.07 secs] 
2016-04-20T22:06:38.342+0000: 4213.475: Total time for which application threads were stopped: 0.0701353 seconds, Stopping threads took: 0.0001600 seconds

我会提请你注意

{Heap before GC invocations=592 (full 0):
   garbage-first heap   total 25600000K, used 22802164K [0x00000001a5800000, 0x00000001a60061a8, 0x00000007c0000000)
[...]
[Eden: 11.7G(11.7G)->0.0B(11.7G) Survivors: 200.0M->200.0M Heap: 21.7G(24.4G)->10.0G(24.4G)]

超过 70% 的堆在此收集之前已被占用。为什么没有触发标记周期?

应用程序继续进行年轻代收集,填满旧区域,最终导致分配失败和冗长的 Full GC。


InitiatingHeapOccupancyPercent 减少到 55 没有明显效果。

在 20 岁时,它确实开始进行混合收集,但仅在大约 80% 的堆被占用时。

【问题讨论】:

  • 它超过了指定的GCTimeRatio,试着放松一下,看看有没有什么不同。
  • @the8472 所以理论上它认为它不能满足吞吐量所以它甚至不尝试?我会尽快返回结果。
  • @the8472 它的 GCTimeRatio 不是 24。请注意,这是日志中的众多样本之一。它基本上能够跟上 99。另外,请参阅我的编辑。

标签: java garbage-collection g1gc


【解决方案1】:

JDK-6976060 建议在年轻 GC 结束时计算对标记周期的需求。取决于它是在年轻 GC 之前还是之后使用占用率统计信息,这可能意味着也可能不意味着在计算 IHOP 时伊甸园空间始终被视为 0% 占用。如果 eden 大小为 45%,这意味着永远无法达到 70% 的占用率,那么最大可能的占用率将是 55%,此时堆将完全填满,对于混合收集来说为时已晚。

但我怀疑这是否真的如此,因为面对动态年轻代的大小调整,这会使文档产生误导,并且 IHOP 调整变得更加困难。使用人工测试用例和手动调整生成大小应该很容易验证这一点。

如果这不是问题,那么 GC pause (GCLocker Initiated GC) (young) 可能会指向 bug 8140597,这在 jdk9b94 中已修复。


更新:Bug 8151176 中的描述确实表明,出于 IHO 百分比计算的目的,它会计算 oldgen 占用率/总堆大小。这意味着年轻代占用被完全忽略,这反过来意味着如果年轻代分数> IHOP,那么它永远不会启动并发循环。

原因是,如果老年代占用率超过当前堆容量的固定百分比,静态 IHOP 就会启动。如果用户或人体工程学决定旧代不能大于触发并发标记的堆容量的那部分,则标记将永远不会开始。

所以目前可用的解决方案是

  • 约束年轻代分数
  • 降低 IHOP 以考虑尽可能少的老一代部分
  • 让 JVM 动态调整 IHOP

更新 2:该错误上的 latest comment 表明该问题已经修复了一段时间,因此该答案应被视为具有历史意义。

【讨论】:

  • 关于你的第一段。它是否考虑两代似乎无关,因为它从不开始一个标记周期。它保持年轻的 gc'ing 直到没有更多的空间。至于bug,我会尝试升级到9,看看会发生什么。
  • @Pillar,对不起,措辞不好。我并不是说 eden 被排除在总体会计之外,而是始终占 0%,因为在年轻收集后它是空的。这意味着如果您的伊甸园已经占堆的 45%,则它无法达到 70% 的占用率。但这是真的,对于您遇到的问题来说,这将是一个相当麻烦的设计,所以我不能确定这真的是问题所在。
  • 你说的很有道理。接下来我会试试这个。我想我已经上升到了 35% 并且有效。 (现在不想在制作中玩,明天上班。)感谢您的帮助。
  • 乍一看,你是对的。当 N=40%,IHOP=50% 时,标记周期从 90% 的满员开始。 N=30%,IHOP=40%,标记周期从70%开始。打算尝试更多组合。
  • 是啊,我跑的任意组合,计算中只使用young GC后的堆。比如 GC 之后,total 20480000K, used 8366146K。这大约是 40%。由于我的 IHOP 也为 40%,因此concurrent-mark-start 紧随其后。你认为这值得打开一个错误吗?我会尝试为他们创建一个 MCVE。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-12
  • 1970-01-01
  • 1970-01-01
  • 2020-09-04
  • 1970-01-01
相关资源
最近更新 更多