OpenJDK8 G1GC不同负载下GC触发阈值相关问题咨询
问题背景
当前运行环境为AdoptOpenJDK 1.8.0_202,固定堆大小10G,GC选用G1,相关配置如下:
openjdk version "1.8.0_202" OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_202-b08) OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.202-b08, mixed mode)
GC参数:
-XX:InitiatingHeapOccupancyPercent=25 -XX:G1MixedGCCountTarget=15 -XX:-ResizePLAB -XX:G1HeapWastePercent=5 -XX:+ParallelRefProcEnabled -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:ConcGCThreads=8 -XX:ParallelGCThreads=20 -XX:G1ReservePercent=20
堆参数:
-Xmx10g -Xms10g
观测到的现象:
- 业务高负载时,整堆内存利用率到60%左右就会触发GC
- 无业务负载时,整堆内存利用率到75%左右才会触发GC
解答
首先明确一个最容易踩的认知误区:你配置的-XX:InitiatingHeapOccupancyPercent=25(简称IHOP),在JDK8u202版本的G1实现中,触发阈值是老年代已使用大小占整个堆总容量的比例,不是整堆已使用大小占总容量的比例。你观测到的整堆利用率是老年代、新生代、Humongous大对象区的占用总和,和IHOP的统计口径不一致,直接对比数值对不上是正常的。
为什么无负载时GC触发阈值到75%
G1触发并发标记周期(即你观测到的全局GC阶段启动)不是只卡IHOP的固定阈值,它自带基于历史运行数据的预测逻辑:
- 业务高负载时,对象分配速率快、Young GC后晋升到老年代的对象多,G1会预判如果等老年代真的涨到IHOP阈值再启动并发标记,并发标记还没执行完,老年代就会被晋升对象占满,触发担保失败。因此会根据历史晋升速率提前启动并发周期,你看到的60%整堆占用触发,就是预测逻辑给出的提前量,负载越高、晋升速度越快,这个提前量就越大。
- 无业务负载时,对象分配速率极低,几乎没有对象持续晋升到老年代,G1的预测模型判定不需要提前启动,会等到老年代占用真正触达IHOP阈值(即老年代占满2.5G空间)才启动并发标记。此时因为没有分配压力,新生代不会被频繁Young GC回收,会自然增长到5G左右的占用,两者相加整堆占用刚好到75%左右,和你观测到的数值完全吻合。
堆占用最高到多少会触发GC
这个没有固定的全局值,要分GC触发场景看:
- 普通Young GC:触发条件是Eden区被占满,此时整堆占用取决于当时老年代的占用情况、当前Eden区的大小,没有统一阈值。
- 并发标记周期(Mixed GC前置阶段):最高触发阈值不会超过
100% - G1ReservePercent,你配置了-XX:G1ReservePercent=20,也就是预留20%(2G)堆空间做对象拷贝的担保空间,所以整堆占用到80%之前必须启动并发标记,否则预留空间不足,很容易出现Evacuation失败退化为Full GC。如果对象分配速率极低、预测模型判定不需要提前启动,触发阈值就会非常贴近80%。 - Full GC:极端情况下(并发周期回收效率跟不上分配速率、预留空间耗尽、大对象分配找不到连续空间),整堆占用会到95%~100%才触发Full GC,此时会出现长时间的STW,属于需要优化的异常场景。
你当前观测到的两种场景下的GC触发阈值都属于G1的正常运行表现,不是参数配置错误。
内容的提问来源于stack exchange,提问作者VJS
相关产品推荐
相关产品推荐

