跳到主要内容

垃圾回收器与GC日志配置

垃圾回收器的选择和配置对Java应用的性能至关重要。本文将深入讲解主流垃圾收集器的特点、选型策略以及GC日志的配置与分析方法。

主流垃圾收集器对比

收集器特性对比

收集器算法线程STW时长适用堆大小JDK版本
Serial复制+标记整理单线程小于100MB全版本
Parallel复制+标记整理多线程中等中等全版本,JDK8默认
CMS标记清除并发中等JDK14移除
G1分区+混合并发可控大于4GBJDK9+默认
ZGC着色指针并发毫秒级TB级以上JDK11+
Shenandoah转发指针并发毫秒级TB级以上JDK12+

垃圾收集器启用参数

# 启用Serial GC(串行收集器)
-XX:+UseSerialGC

# 启用Parallel GC(JDK 8默认,并行收集器)
-XX:+UseParallelGC

# 启用CMS GC(并发标记清除,JDK 14已移除)
-XX:+UseConcMarkSweepGC

# 启用G1 GC(JDK 9+默认)
-XX:+UseG1GC

# 启用ZGC(低延迟收集器,JDK 11+)
-XX:+UseZGC

# 启用Shenandoah GC(JDK 12+)
-XX:+UseShenandoahGC

G1 GC专属调优参数

G1是目前最推荐的垃圾收集器,提供可预测的停顿时间:

G1核心参数

# 设置期望的最大GC停顿时间为200ms
-XX:MaxGCPauseMillis=200

# 设置GC工作线程数为8
-XX:ParallelGCThreads=8

# 设置并发标记线程数
-XX:ConcGCThreads=2

# 设置触发并发GC的堆使用率阈值为45%
-XX:InitiatingHeapOccupancyPercent=45

# 设置Region大小(1MB-32MB之间,2的幂)
-XX:G1HeapRegionSize=4m

# 设置混合回收时老年代Region最大占比
-XX:G1MixedGCLiveThresholdPercent=85

G1参数说明

参数说明默认值建议值
MaxGCPauseMillis期望最大停顿时间200ms根据SLA设置
ParallelGCThreads并行GC线程数CPU核心数CPU核心数
ConcGCThreads并发标记线程数ParallelGCThreads/4保持默认
InitiatingHeapOccupancyPercent触发并发标记的堆占用率45%40-50%
G1HeapRegionSizeRegion大小堆/2048保持默认

GC日志配置

JDK 8 GC日志配置

# 打印详细GC信息
-XX:+PrintGCDetails

# 打印GC时间戳(日期+时间格式)
-XX:+PrintGCDateStamps

# 指定GC日志文件路径(%t为时间戳占位符)
-Xloggc:/var/logs/app/gc-%t.log

# 打印对象晋升年龄分布
-XX:+PrintTenuringDistribution

# GC前后打印堆详细信息
-XX:+PrintHeapAtGC

# 打印引用处理信息(强/软/弱/虚引用)
-XX:+PrintReferenceGC

# 打印应用暂停时间
-XX:+PrintGCApplicationStoppedTime

# 启用GC日志滚动
-XX:+UseGCLogFileRotation

# 保留最近10个日志文件
-XX:NumberOfGCLogFiles=10

# 单个日志文件最大100MB
-XX:GCLogFileSize=100M

JDK 9+ 统一日志配置

JDK 9引入了统一的日志框架(Unified Logging):

# 基础GC日志
-Xlog:gc:gc.log

# 详细GC日志
-Xlog:gc*:gc.log:time,uptime,level,tags

# 带日志轮转
-Xlog:gc*:file=gc.log:time,uptime:filecount=10,filesize=100M

# 分类输出
-Xlog:gc=info,gc+heap=debug,gc+phases=trace:file=gc.log

GC日志输出示例解读

Young GC示例:

2024-11-27T10:15:23.456+0800: [GC (Allocation Failure) 
[PSYoungGen: 512000K->64000K(589824K)]
1024000K->576000K(2038400K), 0.0123456 secs]

解读:

  • GC (Allocation Failure): Eden区分配失败触发的Young GC
  • PSYoungGen: 512000K->64000K(589824K): 新生代从512MB回收到64MB,容量589MB
  • 1024000K->576000K(2038400K): 整个堆从1GB变为576MB,总容量2GB
  • 0.0123456 secs: GC耗时约12ms

Full GC示例:

2024-11-27T10:20:45.789+0800: [Full GC (Metadata GC Threshold) 
[PSYoungGen: 64000K->0K(589824K)]
[ParOldGen: 512000K->256000K(1448576K)]
576000K->256000K(2038400K),
[Metaspace: 245000K->245000K(1280000K)], 0.5432100 secs]

解读:

  • Full GC (Metadata GC Threshold): 元空间达到阈值触发Full GC
  • 新生代完全清空,老年代从512MB减少到256MB
  • 元空间大小未变(245MB),可能存在类加载器泄漏
  • Full GC耗时543ms,需要关注

OOM异常处理配置

OOM发生场景分析

OOM诊断参数配置

# OOM时自动生成堆转储文件
-XX:+HeapDumpOnOutOfMemoryError

# 指定堆转储文件存放路径(%p为进程ID)
-XX:HeapDumpPath=/data/dumps/java_heap_dump_%p.hprof

# OOM时执行自定义脚本(告警/重启等)
-XX:OnOutOfMemoryError="sh /opt/scripts/oom_alert.sh %p"

# 启用GC开销限制(98%时间用于GC且回收<2%堆时抛OOM)
-XX:+UseGCOverheadLimit

堆转储文件分析流程

其他重要调优参数

字符串优化相关

# 启用字符串去重(JDK 8u20+)
-XX:+UseStringDeduplication

# 设置字符串去重的对象年龄阈值
-XX:StringDeduplicationAgeThreshold=3

对象晋升控制

# 设置对象晋升到老年代的年龄阈值(默认15)
-XX:MaxTenuringThreshold=10

# 设置大对象直接进入老年代的阈值(字节)
-XX:PretenureSizeThreshold=1048576

显式GC控制

# 禁止应用代码调用System.gc()触发Full GC
-XX:+DisableExplicitGC

大内存页优化

# 启用大内存页(需操作系统支持)
-XX:+UseLargePages

# 设置大内存页大小
-XX:LargePageSizeInBytes=2m

实战配置示例

高并发Web应用配置

java -server \
-Xms8g -Xmx8g \
-Xmn3g \
-Xss512k \
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=512m \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-Xloggc:/logs/gc-%t.log \
-XX:+UseGCLogFileRotation \
-XX:NumberOfGCLogFiles=14 \
-XX:GCLogFileSize=50M \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/dumps/ \
-XX:+DisableExplicitGC \
-jar web-application.jar

大数据批处理任务配置

java -server \
-Xms16g -Xmx16g \
-XX:MetaspaceSize=512m \
-XX:MaxMetaspaceSize=1g \
-XX:+UseParallelGC \
-XX:ParallelGCThreads=8 \
-XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-Xloggc:/logs/batch-gc.log \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/dumps/ \
-jar batch-processor.jar

微服务应用配置

java -server \
-Xms2g -Xmx2g \
-Xmn800m \
-Xss256k \
-XX:MetaspaceSize=128m \
-XX:MaxMetaspaceSize=256m \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=100 \
-XX:+PrintGCDetails \
-Xloggc:/logs/microservice-gc.log \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/dumps/ \
-jar microservice-app.jar

低延迟应用配置(JDK 11+)

java -server \
-Xms8g -Xmx8g \
-XX:+UseZGC \
-XX:ZCollectionInterval=120 \
-XX:ZFragmentationLimit=10 \
-Xlog:gc*:file=gc.log:time,uptime:filecount=10,filesize=100M \
-XX:+HeapDumpOnOutOfMemoryError \
-jar low-latency-app.jar

GC调优最佳实践

收集器选型建议

场景推荐收集器原因
通用服务端应用G1平衡吞吐和延迟
大数据批处理Parallel最大吞吐量
延迟敏感应用ZGC/Shenandoah毫秒级停顿
小内存应用Serial简单高效
超大堆应用ZGC处理TB级堆

核心调优建议

  1. 务必开启GC日志: 详细日志是定位性能问题的关键依据
  2. 配置OOM自动转储: 发生OOM时自动生成堆转储文件便于事后分析
  3. 选择合适的GC: 根据应用特征选择垃圾收集器
  4. 避免过度优化: 参数调整应基于实际监控数据,避免凭感觉调整
  5. 定期review GC日志: 及时发现潜在问题

垃圾收集器的选择和配置需要根据应用特点和性能目标来决定。没有万能的配置,只有最适合当前应用场景的配置。通过持续监控和调优,可以找到最佳的GC配置方案。