JVM调优 从Full GC报警到性能提升90%的调优实录

背景

  • 事故现场:某社交APP晚高峰突发服务卡顿,用户消息延迟飙升

  • 监控大屏

    • GC时间从50ms/次 → 5秒/次

    • 堆内存锯齿状波动(配Prometheus图表)

    • “Full GC每分钟触发3次,但堆内存却越回收越少?”


一、GC日志分析——JVM的“心电图”解读

1. 日志结构全解析
[GC (Allocation Failure) [PSYoungGen: 614400K->24064K(614400K)]  
 614400K->24128K(2015232K), 0.0345213 secs]  
[Times: user=0.11 sys=0.02, real=0.03 secs]
  • 关键字段解剖

    • Allocation Failure:年轻代分配失败触发GC

    • PSYoungGen:Parallel Scavenge收集器

    • user=0.11:CPU用户态耗时(比real时间更重要!)

2. 三大死亡心电图
  • 内存泄漏波形:每次GC后堆内存持续上涨

  • 晋升风暴波形:老年代在Young GC时频繁被触发

  • GC震荡波形:连续Full GC但内存无法释放


二、参数优化模板——不同场景的“药方库”

1. 电商大促配置(高并发低延迟)
-XX:+UseG1GC  
-XX:MaxGCPauseMillis=200  
-XX:InitiatingHeapOccupancyPercent=45  
-XX:G1ReservePercent=15  
-XX:SurvivorRatio=6          // 提升存活区空间  
-XX:+ParallelRefProcEnabled // 加速弱引用处理
2. 大数据计算配置(吞吐优先)
-XX:+UseParallelGC  
-XX:ParallelGCThreads=32     // 与CPU核数一致  
-XX:GCTimeRatio=99           // GC时间占比<1%  
-XX:+UseAdaptiveSizePolicy   // 自动调整分区  
-XX:MaxTenuringThreshold=15  // 对象充分熬代
3. 容器环境配置(限Docker/K8s)
-XX:+UseContainerSupport  
-XX:MaxRAMPercentage=70.0    // 避免超限被杀  
-XX:ActiveProcessorCount=4   // 显式指定CPU数  
-Xloggc:/opt/gc_%t.log       // 时间戳命名防覆盖

三、实战诊断——Arthas在线剖腹产

1. 内存泄漏定位
  • 步骤分解

    1. jmap -dump:format=b,file=heap.hprof

    2. MAT中分析Dominator Tree

    3. 定位到ConcurrentHashMap$Node[]占1.2GB

    4. 追溯代码发现本地缓存未设TTL

2. GC效率优化(G1调优实操)
# 实时观察混合GC效果
jstat -gcutil  1000

# 关键指标解读:
#  M => Metaspace使用率
#  CCS => 压缩类空间
#  YGC/YGCT => YoungGC次数/耗时

四、避坑指南——血泪换来的教训

1. 参数作死三件套
  • -Xmn设置过大 → 导致老年代空间不足

  • -XX:+DisableExplicitGC → 引发NIO堆外内存泄漏

  • -XX:+AggressiveOpts → 不同版本效果不可控

2. 日志分析三大错觉
  • “GC时间正常” → 但user时间远超real时间(CPU争抢)

  • “没有Full GC” → 但CMS并发失败触发STW

  • “内存正常回收” → 但堆外内存持续增长

你可能感兴趣的:(jvm,java,java性能优化,jvm,java)