最先进的 GC 并不是满足所有应用场景的 GC,要因地制宜,按需选择;
1. Epsilon GC
一个不干活
的 GC;
一个垃圾收集器除了垃圾收集之外,内存的管理与布局、对象分配、与解释器的协作、与编译器的协作、与监控子系统协作等也是必须关注的内容;从 JDK 10 开始,为了隔离垃圾收集器与 JVM 解释、编译、监控等子系统的关系,RedHat 提出了垃圾收集器的统一接口,Epsilon
是这个接口的有效性验证和参考实现,同时也用于需要剥离垃圾收集器影响的性能测试和压力测试(对比效果);
如果应用只运行数分钟甚至数秒,只要 JVM 能正确分配内存,在 Heap 耗尽之前退出,那运行负载极小、没有任何回收行为 的 Epsilon 是很恰当的选择;
2. 选择 GC 的考虑因素
- 应用程序的主要关注点(停顿时间、吞吐量、内存占用),SLA 应用关注停顿时间,数据分析等后台任务关注吞吐量(计算速度),小型嵌入式或客户端应用的内存占用不可忽视;
- 运行应用的基础设施条件(硬件规格、性能,系统平台等);
- JDK 版本(OracleJDK、OpenJDK、ZingJDK/Zulu、OpenJ9 等);
3. VM 及 GC 日志
HotSpot 从 JDK 9 开始通过 -Xlog
管理 VM 日志(此前的日志设置是各 GC 独立解决);
-Xlog[:[selector][:[output][:decorators][:output-options]]]
selector
(选择器),有标签(Tag,功能模块的名称,垃圾收集器的标签名是 gc)和日志级别(Level,决定了日志输出的详细程度,默认 Info 级别)共同组成;Tag
: add,age,alloc,annotation,aot,arguments,attach,barrier,biasedlocking,blocks,bot,breakpoint,bytecode 等;Level
: Trace,Debug,Info,Warning,Error,Off;
decorators
(修饰器),为每行日志输出附加额外内容,如 pid、tid、level、tags、time、uptime、timemillis、uptimemillis、timenanos、uptimenanos(默认是:uptime、level、tags);
# eg.
[3.080s][info][gc,cpu] GC(5) User=0.03s Sys=0.00s Real=0.01s
JDK 9 前后日志参数对比
JDK 9 前日志参数 | JDK 9 后配置形式 |
---|---|
G1PrintHeapRegions | Xlog:gc+region=trace |
G1PrintRegionLivenessInfo | Xlog:gc+liveness=trace |
G1SummarizeConcMark | Xlog:gc+marking=trace |
G1SummarizeRSetStats | Xlog:gc+remset*=trace |
GCLogFileSize,NumberOfGCLogFiles,UseGCLogFileRotation | Xlog:gc*:file=::filecount=,filesize= |
PrintAdaptiveSizePolicy | Xlog:gc+ergo*=trace |
PrintClassHistogramAfterFullGC | Xlog:classhisto*=trace |
PrintClassHistogramBeforeFullGC | XLog:classhisto*=trace |
PrintGCApplicationConcurrentTime | Xlog:safepoint |
PrintGCApplicationStoppedTime | Xlog:safepoint |
PrintGCDateStamps | 使用 time 修饰器 |
PrintGCTaskTimeStamps | 使用 uptime 修饰器 |
PrintHeapAtGC | Xlog:gc+heap=debug |
PrintHeapAtGCExtended | Xlog:gc+heap=trace |
PrintJNIGCStalls | Xlog:gc+jni=debug |
PrintOldPLAB | Xlog:gc+plab=trace |
PrintParallelOldGCPhaseTimes | Xlog:gc+phases=trace |
PrintPLAB | Xlog:gc+plab=trace |
PrintPromotionFailure | Xlog:gc+promotion=debug |
PrintReferenceGC | Xlog:gc+ref=debug |
PrintStringDeduplicationStatistics | Xlog:gc+stringdedup |
PrintTaskqueue | Xlog:gc+task+stats=trace |
PrintTenuringDistribution | Xlog:gc+age=trace |
PrintTerminationStats | Xlog:gc+task+stats=debug |
PrintTLAB | Xlog:gc+tlab=trace |
TraceAdaptiveGCBoundary | Xlog:heap+ergo=debug |
TraceDynamicGCThreads | Xlog:gc+task=trace |
TraceMetadataHumongousAllocation | Xlog:gc+metaspace+alloc=debug |
G1TraceConcRefinement | XLog+gc+refine=debug |
G1TraceEagerReclaimHumongousObjects | Xlog:gc+humongous=debug |
G1TraceStringSymbolTableScrubbing | Xlog:gc+stringtable=trace |
获取 GC 信息日志示例
- 查看 GC 基本信息
# JDK 9 之前
-XX:+PrintGC
# JDK 9 后
java -Xlog:gc GCTest
- 查看 GC 详细信息
# JDK 9 之前
-XX:+PrintGCDetails
# JDK 9 后
java -Xlog:gc* GCTest
- 查看 GC 前后的 Heap、Method Area 可用容量变化
# JDK 9 之前
-XX:+PrintHeapAtGC
# JDK 9 后
java -Xlog:gc+heap=debug GCTest
- 查看 GC 过程中用户线程并发时间和停顿时间
# JDK 9 之前
-XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime
# JDK 9 后
java -Xlog:safepoint GCTest
- 查看收集器 Ergonomics 机制自动调节(堆分代大小、收集目标等调节)的信息
# JDK 9 之前
-XX:+PrintAdaptiveSizePolicy
# JDK 9 后
java -Xlog:gc+ergo*=trace GCTest
- 查看 GC 后存活对象的年龄分布
# JDK 9 之前
-XX:+PrintTenuringDistribution
# JDK 9 后
java -Xlog:gc+age=trace GCTest
4. GC 参数汇总
参数 | 说明 |
---|---|
UseSerialGC | VM 在 Client 模式下的默认值,开启此开关,将使用 Serial + Serial Old 的 GC 组合进行内存回收 |
UseParNewGC | 打开此开关,将使用 ParNew + Serial Old 的 GC 组合进行内存回收,JDK 9 后不在支持 |
UseConcMarkSweepGC | 打开此开关,将使用 ParNew + CMS + Serial Old 的 GC 组合,Serial Old 作为 CMS 出现 Concurrent Mode Failure 失败后的后备 GC |
UseParallelGC | 打开此开关,将使用 Parallel Scavenge + Serial Old 的 GC 组合,JDK 9 之前 Server 模式的默认选项 |
UseParallelOldGC | 打开此开关,将使用 Parallel Scavenge + Parallel Old 的 GC 组合 |
SurvivorRatio | 新生代中 Eden:Survivor 的容量比值,默认是 8,表示 Eden:Survivor = 8:1 |
PretenureSizeThreshold | 直接晋升老年代的对象大小,大于这个参数的对象将直接在老年代分配 |
MaxTenuringThreshold | 晋升老年代的对象年龄,每 Minor GC 一次,对象的年龄加 1,超过这个参数值时,对象进入老年代 |
UseAdaptiveSizePolicy | 动态调整 Java Heap 中各区域的大小,以及晋升老年代的年龄 |
HandlePromotionFailure | 是否允许分配担保失败,即老年代的剩余空间不足以应付新生代整个 Eden 和 Survivor 的所有对象都存活的情况 |
ParallelGCThreads | (STW 期间,)给并行 GC 设置 GC 线程个数 |
GCTimeRatio | GC 时间占总时间的比率,默认 99,表示 1/(1+99) 的时间可用于 GC,仅对 Parallel Scavenge 有效 |
MaxGCPauseMillis | 设置 GC 最大停顿时间 仅对 Parallel Scavenge 有效 |
CMSInitiatingOccupancyFraction | 设置 CMS 在老年代空间被用多少后触发 GC,默认 68%,仅对 CMS 有效 |
UseCMSCompactAtFullCollection | 设置 CMS 在完成 GC 后是否进行一次碎片整理,仅对 CMS 有效,JDK 9 废弃 |
UseG1GC | 使用 G1 GC,JDK 9 后成为默认选项 |
G1HeapRegionSize=n | 设置 Region 大小,并非最终值 |
MaxGCPauseMillis | 设置 G1 GC 的停顿目标时间,默认 200ms,不是强制时间限制 |
G1NewSizePercent | 设置 G1 新生代最小值,默认 5% |
G1MaxNewSizePrecent | 设置 G1 新生代最大值,默认 60% |
ConcGCThreads=n | 并发标记,并发整理的执行线程数,不同 GC 的不同阶段含义可能不同 |
InitiatingHeapOccupancyPercent | 设置触发标记周期的 Java Heap 占用率阈值,默认 45%,这里的占比指 non_young_capacity_bytes(old+humongous) |
UseShenandoahGC | 使用 Shenandoah GC,需要与 -XX:+UnlockExperimentalVMOptions 使用,仅对 OpenJDK 12 以上或支持的 Backport 版本有效,对 OracleJDK 无效 |
ShenandoahGCHeuristics | Shenandoah 合适启动一次 GC,可选项为 adaptive、static、compact、passive、aggressive |
UseZGC | 使用 ZGC,在 JDK 15 前需要配合 -XX:+UnlockExperimentalVMOptions 使用 |
UseNUMA | 启用 NUMA 内存分配支持,只支持 Parallel 和 ZGC,G1 在后续也可能支持 |
上一篇:「JVM 内存管理」2 款低延迟的 GC
下一篇:「JVM 内存管理」内存分配与回收策略
PS:感谢每一位志同道合者的阅读,欢迎关注、评论、赞!
参考资料:
- [1]《深入理解 Java 虚拟机》