序
本文主要研究下JEP 248: Make G1 the Default Garbage Collector
默认垃圾收集器
java9废弃了CMS垃圾收集器,并把G1提升为默认垃圾收集器,替代了原来的吞吐优先的ParallelOldGC
Region
G1相对于之前的垃圾收集器最大的不同是引入了Region。G1虽然也是基于分代机制,但是各个generation的空间不再连续,如下图:
可以用-XX:G1HeapRegionSize=16m来指定Region Size,注意它必须是2的乘方,范围在1MB到32MB之间。目标是根据最小的Java堆大小划分出约2048 个区域.
Min Heap Size | Region Size |
---|---|
heap < 4GB | 1MB |
4GB <= heap < 8GB | 2MB |
8GB <= heap < 16GB | 4MB |
16GB <= heap < 32GB | 8MB |
32GB <= heap < 64GB | 16MB |
64GB <= heap | 32MB |
如果没有明确指定,则自动根据Heap Size来指定,其对应关系如下:
Min Heap Size | Region Size |
---|---|
heap < 4GB | 1MB |
4GB <= heap < 8GB | 2MB |
8GB <= heap < 16GB | 4MB |
16GB <= heap < 32GB | 8MB |
32GB <= heap < 64GB | 16MB |
64GB <= heap | 32MB |
Humongous Object/ Humongous区域
- 对于G1 GC,任何超过区域一半大小的对象都被视为“巨型对象”。
- 此类对象直接被分配到老年代中的“巨型区域”。
- 这些巨型区域是一个连续的区域集。StartsHumongous 标记该连续集的开始,ContinuesHumongous 标记它的延续。
巨型对象默认直接会被分配在年老代,但是如果它是一个短期存在的巨型对象,就会对垃圾收集器造成负面影响。为了解决这个问题,G1划分了一个Humongous区,它用来专门存放巨型对象。如果一个H区装不下一个巨型对象,那么G1会寻找连续的H分区来存储。为了能找到连续的H区,有时候不得不启动Full GC。
GC事件及触发
GC event
- Minor GC event
正常的young gc
- Mixed GC event
Minor GC + (# reclaimable Tenured regions / -XX:G1MixedGCCountTarget) regions of Tenured
- Full GC event
All regions evacuated,通常Humongous object太多会耗尽空间,导致Full GC
- Minor/Mixed + To-space exhaustion
Minor/Mixed + rollback + Full GC
触发时机
- Eden满/空间不够
- 剩余空间不够容纳一个Humongous object
- Humongous object分配成功,同时符合一些GC条件
- 外部命令触发(jcmd, jmap, Runtime.gc())
G1 GC分类
主要分为:Minor GC,Mixed/Old GC
Minor GC/Young GC(STW
)
Young GC主要是对Eden区进行GC,它在Eden空间不够时会被触发。Eden空间的数据移动到Survivor空间中,如果Survivor空间不够,Eden空间的部分数据会直接晋升到年老代空间。From Survivor区的数据移动到To Survivor区中,也有部分数据晋升到老年代空间中。
阶段 | 执行动作 |
---|---|
阶段1 根扫描 | 静态和本地对象被扫描 |
阶段2 更新RS | 处理dirty card队列更新RS |
阶段3 处理RS | 检测从年轻代指向年老代的对象 |
阶段4 对象拷贝 | 拷贝存活的对象到survivor/old区域 |
阶段5 处理引用队列 | 软引用,弱引用,虚引用处理 |
其阶段主要如下:
阶段 | 执行动作 |
---|---|
阶段1 根扫描 | 静态和本地对象被扫描 |
阶段2 更新RS | 处理dirty card队列更新RS |
阶段3 处理RS | 检测从年轻代指向年老代的对象 |
阶段4 对象拷贝 | 拷贝存活的对象到survivor/old区域 |
阶段5 处理引用队列 | 软引用,弱引用,虚引用处理 |
Mixed/Old GC(STW
)
主要是对年老代进行并发标记然后进行GC,其中部分阶段涉及到ygc,同时既有ygc及old gc的部分称为mixed gc。
阶段 | 执行动作 |
---|---|
(1) Initial Mark(Stop the World Event) 初始标记阶段 | 在此阶段G1 GC对根进行标记。该阶段与常规的 (STW) 年轻代垃圾回收密切相关。日志标记为Pause Initial Mark (G1 Evacuation Pause). |
(2) Root Region Scanning 根区域扫描阶段 | G1 GC在初始标记的存活区扫描对老年代的引用,并标记被引用的对象。该阶段与应用程序(非 STW)同时运行,并且只有完成该阶段后,才能开始下一次STW年轻代垃圾回收。 |
(3) Concurrent Marking 并发标记阶段 | G1 GC在整个堆中查找可访问的(存活的)对象。该阶段与应用程序同时运行,可以被 STW 年轻代垃圾回收中断。 |
(4) Remark(Stop the World Event) 重新标记阶段 | 该阶段是STW回收,帮助完成标记周期。G1 GC清空SATB缓冲区,跟踪未被访问的存活对象,并执行引用处理。 |
(5) Copying(Stop the World Event) / Cleanup(Stop the World Event and Concurrent) 拷贝/清理阶段 | 在这个最后阶段,G1 GC为了更快进行垃圾回收,会选择那些存活率低的region进行拷贝,即evacuate或者拷贝存活对象到新的空闲的regions,然后清理回收该region,此时会STW,如果是在年轻代产生的,则日志标记为Pause Young (G1 Evacuation Pause),如果年轻代和年老代都进行这个动作,则日志标记为Pause Mixed (G1 Evacuation Pause). |
并发标记周期(Concurrent Marking Cycle Phases)阶段如下:
阶段 | 执行动作 |
---|---|
(1) Initial Mark(Stop the World Event) 初始标记阶段 | 在此阶段G1 GC对根进行标记。该阶段与常规的 (STW) 年轻代垃圾回收密切相关。日志标记为Pause Initial Mark (G1 Evacuation Pause). |
(2) Root Region Scanning 根区域扫描阶段 | G1 GC在初始标记的存活区扫描对老年代的引用,并标记被引用的对象。该阶段与应用程序(非 STW)同时运行,并且只有完成该阶段后,才能开始下一次STW年轻代垃圾回收。 |
(3) Concurrent Marking 并发标记阶段 | G1 GC在整个堆中查找可访问的(存活的)对象。该阶段与应用程序同时运行,可以被 STW 年轻代垃圾回收中断。 |
(4) Remark(Stop the World Event) 重新标记阶段 | 该阶段是STW回收,帮助完成标记周期。G1 GC清空SATB缓冲区,跟踪未被访问的存活对象,并执行引用处理。 |
(5) Copying(Stop the World Event) / Cleanup(Stop the World Event and Concurrent) 拷贝/清理阶段 | 在这个最后阶段,G1 GC为了更快进行垃圾回收,会选择那些存活率低的region进行拷贝,即evacuate或者拷贝存活对象到新的空闲的regions,然后清理回收该region,此时会STW,如果是在年轻代产生的,则日志标记为Pause Young (G1 Evacuation Pause),如果年轻代和年老代都进行这个动作,则日志标记为Pause Mixed (G1 Evacuation Pause). |
核心的阶段主要是Concurrent Marking Phase、Remark Phase、Copying/Cleanup Phase
相关参数
参数 | 含义 |
---|---|
-XX:G1HeapRegionSize=n | 设置Region大小,并非最终值 |
-XX:MaxGCPauseMillis=200 | 设置G1收集过程目标时间,默认值200ms,不是硬性条件 |
-XX:G1NewSizePercent=5 | 新生代最小值,默认值5% |
-XX:G1MaxNewSizePercent=60 | 新生代最大值,默认值60% |
-XX:ParallelGCThreads=n | STW期间,并行GC线程数,其值与逻辑处理器的数量相同,最多为8。如果逻辑处理器不止八个,则将n为逻辑处理器数的5/8 |
-XX:ConcGCThreads=n | 并发标记阶段的并行标记线程数,ParallelGCThreads的 1/4 左右 |
-XX:InitiatingHeapOccupancyPercent=45 | 设置触发标记周期的Java堆占用率阈值。默认值是45%。这里的java堆占比指的是non_young_capacity_bytes,包括old+humongous |
-XX:G1MixedGCLiveThresholdPercent=65 | 为混合垃圾回收周期中要包括的旧区域设置占用率阈值。默认占用率为 65% |
-XX:G1HeapWastePercent=10 | 如果可回收百分比小于此值,JVM不会启动混合垃圾回收周期。默认值是10% |
-XX:G1OldCSetRegionThresholdPercent=10 | 设置混合垃圾回收期间要回收的最大旧区域数。默认值是Java堆的10% |
-XX:G1MixedGCCountTarget=8 | 设置标记周期完成后,对存活数据上限为 G1MixedGCLIveThresholdPercent 的旧区域执行混合垃圾回收的目标次数。默认8次混合垃圾回收,混合回收的目标是要控制在此目标次数以内 |
-XX:G1ReservePercent=10 | 设置作为空闲空间的预留内存百分比,以降低目标空间溢出的风险。默认值是10%。增加或减少百分比时,请确保对总的Java 堆调整相同的量 |
避免使用-Xmn选项或-XX:NewRatio等其他相关选项显式设置年轻代大小,固定年轻代的大小会禁用掉暂停时间(MaxGCPauseMillis)目标。
GC日志实例
简版使用-Xlog:gc=info,详细版使用-Xlog:gc*=info
ygc
- 简版
[0.317s][info][gc] GC(37) Pause Young (G1 Evacuation Pause) 7M->6M(10M) 0.511ms
[0.324s][info][gc] GC(40) Pause Young (G1 Evacuation Pause) 7M->6M(10M) 0.709ms
- 详细版
[0.011s][info][gc,heap] Heap region size: 1M
[0.012s][info][gc ] Using G1
[0.012s][info][gc,heap,coops] Heap address: 0x00000007bf600000, size: 10 MB, Compressed Oops mode: Zero based, Oop shift amount: 3
[0.170s][info][gc,start ] GC(0) Pause Young (G1 Evacuation Pause)
[0.170s][info][gc,task ] GC(0) Using 8 workers of 8 for evacuation
[0.172s][info][gc,phases ] GC(0) Pre Evacuate Collection Set: 0.0ms
[0.172s][info][gc,phases ] GC(0) Evacuate Collection Set: 1.5ms
[0.172s][info][gc,phases ] GC(0) Post Evacuate Collection Set: 0.1ms
[0.172s][info][gc,phases ] GC(0) Other: 0.1ms
[0.172s][info][gc,heap ] GC(0) Eden regions: 4->0(2)
[0.172s][info][gc,heap ] GC(0) Survivor regions: 0->1(1)
[0.172s][info][gc,heap ] GC(0) Old regions: 0->1
[0.172s][info][gc,heap ] GC(0) Humongous regions: 0->0
[0.172s][info][gc,metaspace ] GC(0) Metaspace: 5982K->5982K(1056768K)
[0.172s][info][gc ] GC(0) Pause Young (G1 Evacuation Pause) 4M->1M(10M) 1.718ms
[0.172s][info][gc,cpu ] GC(0) User=0.01s Sys=0.00s Real=0.00s
old gc
- 简版
[0.321s][info][gc] GC(38) Pause Initial Mark (G1 Evacuation Pause) 7M->6M(10M) 0.601ms
[0.321s][info][gc] GC(39) Concurrent Cycle
[0.324s][info][gc] GC(40) Pause Young (G1 Evacuation Pause) 7M->6M(10M) 0.709ms
[0.326s][info][gc] GC(39) Pause Remark 7M->7M(10M) 0.623ms
[0.326s][info][gc] GC(39) Pause Cleanup 7M->7M(10M) 0.104ms
[0.326s][info][gc] GC(39) Concurrent Cycle 5.398ms
[0.327s][info][gc] GC(41) Pause Young (G1 Evacuation Pause) 7M->6M(10M) 0.512ms
[0.331s][info][gc] GC(42) To-space exhausted
[0.331s][info][gc] GC(42) Pause Mixed (G1 Evacuation Pause) 7M->7M(10M) 1.190ms
[0.334s][info][gc] GC(43) Pause Initial Mark (G1 Evacuation Pause) 8M->7M(10M) 0.637ms
[0.334s][info][gc] GC(44) Concurrent Cycle
[0.338s][info][gc] GC(45) Pause Young (G1 Evacuation Pause) 8M->7M(10M) 0.553ms
[0.340s][info][gc] GC(44) Pause Remark 8M->8M(10M) 0.582ms
[0.341s][info][gc] GC(44) Pause Cleanup 8M->8M(10M) 0.100ms
[0.341s][info][gc] GC(44) Concurrent Cycle 6.195ms
- 详细版
[0.942s][info][gc,start ] GC(192) Pause Initial Mark (G1 Evacuation Pause)
[0.942s][info][gc,task ] GC(192) Using 8 workers of 8 for evacuation
[0.942s][info][gc,phases ] GC(192) Pre Evacuate Collection Set: 0.0ms
[0.942s][info][gc,phases ] GC(192) Evacuate Collection Set: 0.4ms
[0.942s][info][gc,phases ] GC(192) Post Evacuate Collection Set: 0.0ms
[0.942s][info][gc,phases ] GC(192) Other: 0.0ms
[0.942s][info][gc,heap ] GC(192) Eden regions: 0->0(1)
[0.942s][info][gc,heap ] GC(192) Survivor regions: 0->0(1)
[0.942s][info][gc,heap ] GC(192) Old regions: 10->10
[0.942s][info][gc,heap ] GC(192) Humongous regions: 0->0
[0.942s][info][gc,metaspace ] GC(192) Metaspace: 5993K->5993K(1056768K)
[0.942s][info][gc ] GC(192) Pause Initial Mark (G1 Evacuation Pause) 9M->9M(10M) 0.530ms
[0.942s][info][gc,cpu ] GC(192) User=0.00s Sys=0.00s Real=0.00s
[0.942s][info][gc ] GC(193) Concurrent Cycle
[0.942s][info][gc,marking ] GC(193) Concurrent Clear Claimed Marks
[0.942s][info][gc,marking ] GC(193) Concurrent Clear Claimed Marks 0.004ms
[0.942s][info][gc,marking ] GC(193) Concurrent Scan Root Regions
[0.942s][info][gc,marking ] GC(193) Concurrent Scan Root Regions 0.003ms
[0.942s][info][gc,marking ] GC(193) Concurrent Mark (0.942s)
[0.942s][info][gc,marking ] GC(193) Concurrent Mark From Roots
[0.942s][info][gc,task ] GC(193) Using 2 workers of 2 for marking
[0.942s][info][gc,start ] GC(194) Pause Full (Allocation Failure)
[0.943s][info][gc,phases,start] GC(194) Phase 1: Mark live objects
[0.946s][info][gc,stringtable ] GC(194) Cleaned string and symbol table, strings: 3222 processed, 0 removed, symbols: 25923 processed, 0 removed
[0.946s][info][gc,phases ] GC(194) Phase 1: Mark live objects 3.168ms
[0.946s][info][gc,phases,start] GC(194) Phase 2: Compute new object addresses
[0.946s][info][gc,phases ] GC(194) Phase 2: Compute new object addresses 0.418ms
[0.946s][info][gc,phases,start] GC(194) Phase 3: Adjust pointers
[0.949s][info][gc,phases ] GC(194) Phase 3: Adjust pointers 2.706ms
[0.949s][info][gc,phases,start] GC(194) Phase 4: Move objects
[0.949s][info][gc,phases ] GC(194) Phase 4: Move objects 0.005ms
[0.949s][info][gc,task ] GC(194) Using 8 workers of 8 to rebuild remembered set
[0.951s][info][gc,heap ] GC(194) Eden regions: 0->0(1)
[0.951s][info][gc,heap ] GC(194) Survivor regions: 0->0(1)
[0.951s][info][gc,heap ] GC(194) Old regions: 10->10
[0.951s][info][gc,heap ] GC(194) Humongous regions: 0->0
[0.951s][info][gc,metaspace ] GC(194) Metaspace: 5993K->5993K(1056768K)
[0.951s][info][gc ] GC(194) Pause Full (Allocation Failure) 9M->9M(10M) 8.955ms
[0.951s][info][gc,cpu ] GC(194) User=0.01s Sys=0.00s Real=0.01s
小结
G1收集器博大精深,有待进一步实践进行深入理解研究。
doc
- JDK 9 features
- JEP 248: Make G1 the Default Garbage Collector
- JEP 291: Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector
- JEP 278: Additional Tests for Humongous Objects in G1
- Getting Started with the G1 Garbage Collector
- G1GC Fundamentals: Lessons from Taming Garbage Collection
- 垃圾优先型垃圾回收器调优
- 深入理解 Java G1 垃圾收集器
- Java 9中的GC调优基础
- Java Hotspot G1 GC的一些关键技术
- Garbage First G1收集器 理解和原理分析
- Tuning Java Garbage Collection for Apache Spark Applications