文章目录
- 一、GC 日志的核心格式
- 1. 通用核心字段解析
- 2. 主流收集器的典型日志格式
- (1)Parallel GC(并行收集器,默认吞吐量优先)
- (2)CMS GC(低延迟收集器)
- (3)G1 GC(默认低延迟收集器,JDK9+)
- 二、如何通过GC日志分析服务问题?
- 步骤1:解析基础信息,明确分析前提
- 步骤2:统计核心指标,识别异常
- 指标统计方法:
- 步骤3:定位常见问题场景(日志特征+根因)
- 示例:问题分析实战
- 三、基于GC日志的性能优化策略
- 1. 应用层优化(治本,优先做)
- 2. JVM参数优化(治标,针对性调)
- (1)堆内存配置
- (2)GC线程数
- (3)针对STW优化
- 3. 收集器选型优化
- 四、总结
GC 日志是 JVM 垃圾回收过程的“行为记录”,包含回收类型、内存变化、耗时、触发原因等核心信息,是定位内存问题、优化 JVM 性能的核心依据。以下从「日志格式」「问题分析方法」「性能优化策略」三部分系统讲解,结合主流收集器(Parallel/CMS/G1)的实战案例。
一、GC 日志的核心格式
GC 日志无“统一标准格式”,但遵循「通用核心结构 + 收集器特有扩展」,所有日志行的核心字段可归纳为:[时间戳][日志级别/标签][GC事件] 核心信息(内存变化/时长/触发原因)
1. 通用核心字段解析
| 字段类型 | 示例/格式 | 含义解读 |
|---|---|---|
| 时间戳 | [0.213s]/[2025-12-27T10:00:00.123+0800] | 前者:JVM 启动后累计秒数;后者:绝对时间(需配置-XX:+PrintGCDateStamps) |
| 日志标签 | [info][gc,start]/[gc,heap]/[gc,time] | 标签维度:gc,start(GC启动)、gc,heap(堆内存)、gc,time(耗时)、gc,cause(触发原因) |
| GC 事件 | GC(0) Pause Young/Full GC | GC(0):第0次GC;Pause Young:年轻代GC(STW);Full GC:全堆回收 |
| 内存变化 | Eden: 1024M->0M (1024M) | 格式:区域名:回收前->回收后(总容量),如Eden区从1024M清空到0M |
| 耗时 | Pause Young: 50.2ms/CMS-concurrent-mark: 200ms | STW阶段(Pause开头)是应用暂停时长;并发阶段无Pause,仅记录阶段耗时 |
| 触发原因 | (Allocation Failure)/(G1 Evacuation Pause) | Allocation Failure:内存分配失败;Evacuation Pause:G1撤离暂停 |
2. 主流收集器的典型日志格式
(1)Parallel GC(并行收集器,默认吞吐量优先)
# Young GC(Minor GC) [0.500s][info][gc] GC(1) PSYoungGen: 1024M->0M (1536M) ParOldGen: 512M->520M (2048M) Metaspace: 100M->100M (1024M) [0.500s][info][gc,time] GC(1) User=0.10s Sys=0.02s Real=0.05s # Full GC [10.000s][info][gc] GC(20) Full GC PSYoungGen: 800M->0M (1536M) ParOldGen: 1900M->1800M (2048M) Metaspace: 200M->200M (1024M) [10.000s][info][gc,time] GC(20) User=1.50s Sys=0.10s Real=1.60s- 核心特征:
PSYoungGen(并行年轻代)、ParOldGen(并行老年代),无并发阶段,所有GC均STW; Real:实际耗时(STW时长),User/Sys:CPU耗时。
(2)CMS GC(低延迟收集器)
# 初始标记(STW,短) [2.000s][info][gc] GC(5) CMS-initial-mark: 800M(2048M) [2.000s][info][gc,time] GC(5) Real=0.01s # 并发标记(无STW) [2.001s][info][gc] GC(5) CMS-concurrent-mark-start [2.200s][info][gc] GC(5) CMS-concurrent-mark: 0.199s # 重新标记(STW,略长) [2.200s][info][gc] GC(5) CMS-remark [2.200s][info][gc,time] GC(5) Real=0.03s # 并发清理(无STW) [2.201s][info][gc] GC(5) CMS-concurrent-sweep-start [2.400s][info][gc] GC(5) CMS-concurrent-sweep: 0.199s # 异常:并发失败触发Full GC [5.000s][info][gc] GC(10) Full GC (CMS Concurrent Mode Failure) CMS: 1980M->1800M(2048M) [5.000s][info][gc,time] GC(10) Real=3.00s- 核心特征:分4个阶段,仅「初始标记/重新标记」STW,其余并发;异常时触发串行Full GC,STW极长。
(3)G1 GC(默认低延迟收集器,JDK9+)
# Young GC(Evacuation Pause,STW) [0.213s][info][gc,start] GC(0) Pause Young (Normal) (G1 Evacuation Pause) [0.213s][info][gc,heap] Eden regions: 8->0 (8) Survivor regions: 1->1 (2) Old regions: 4->5 (16) [0.213s][info][gc,time] GC(0) Pause Young: 5.0ms # Mixed GC(回收年轻代+部分老年代,STW) [5.000s][info][gc,start] GC(10) Pause Mixed (G1 Evacuation Pause) [5.000s][info][gc,heap] Region occupation: 60% [5.000s][info][gc,time] GC(10) Pause Mixed: 10.0ms # 异常:晋升失败 [8.000s][info][gc,erro] GC(15) Promotion Failure [8.000s][info][gc,start] GC(16) Pause Full (Allocation Failure) [8.000s][info][gc,time] GC(16) Pause Full: 1200.0ms- 核心特征:按Region划分堆,日志含Region数量/占用率,Young/Mixed GC为主要STW阶段,异常时触发Full GC。
二、如何通过GC日志分析服务问题?
分析核心逻辑:先定基准→找异常指标→定位根因,以下是标准化分析步骤 + 常见问题场景拆解。
步骤1:解析基础信息,明确分析前提
首先从日志开头/关键行提取基础配置,避免“无基准的盲目分析”:
- 收集器类型:从日志关键词判断(PSYoungGen=Parallel、CMS-=CMS、G1 Evacuation=G1);
- 堆配置:日志开头通常打印
-Xms/-Xmx/-Xmn(如Initial Heap Size: 2048M, Max Heap Size: 2048M); - 核心参数:如G1的
IHOP(初始化堆占用百分比)、CMS的CMSInitiatingOccupancyFraction; - 业务基准:结合业务场景定阈值(如电商秒杀允许STW≤100ms,后台任务允许≤500ms)。
步骤2:统计核心指标,识别异常
重点统计以下指标,对比基准值判断是否异常:
| 核心指标 | 通用基准值(参考) | 异常判定 |
|---|---|---|
| Young GC 频次 | 10~30秒发生一次 | <1秒发生一次(频繁) |
| Young GC STW 时长 | <50ms(G1)/<100ms(Parallel) | >100ms(超标) |
| Full GC 频次 | <1次/小时(非强制) | >1次/10分钟(频繁) |
| Full GC STW 时长 | 尽量避免 | >500ms(严重卡顿) |
| 老年代占用率(GC后) | <70% | >90%(内存不足) |
| 堆分配速率 | 与业务匹配 | 突增(如每秒百MB) |
指标统计方法:
- 手动统计:按时间戳计算GC间隔(如两次Young GC的时间差)、累加STW时长;
- 工具辅助:GCEasy(在线分析)、GCViewer(本地工具)、VisualVM(可视化),自动生成频次/时长/内存趋势图。
步骤3:定位常见问题场景(日志特征+根因)
| 问题场景 | 核心日志特征 | 根本原因 |
|---|---|---|
| Young GC 频繁 | 每秒多次Young GC,Eden区快速被占满(如Eden:1024M->0M,0.5秒后再次触发) | 新生代(Eden)过小;应用创建大量临时对象(如字符串拼接、未复用集合);分配速率过高 |
| Young GC STW 过长 | Pause Young: 200ms+,日志含Humongous Allocation(G1) | 新生代过大(STW扫描时间长);大对象直接进入新生代;GC线程数不足(-XX:ParallelGCThreads) |
| Full GC 频繁 | 多次Pause Full,GC后老年代占用率>90%,触发原因Allocation Failure | 内存泄漏(对象未释放);老年代碎片化(CMS/G1);大对象直接进入老年代;堆配置过小 |
| Full GC STW 极长 | Pause Full: 1s+,CMS触发Concurrent Mode Failure,G1触发Promotion Failure | 老年代耗尽;并发回收速度赶不上内存分配速度;堆碎片化严重 |
| 内存泄漏 | Full GC后老年代占用率几乎不变,堆占用持续上涨直至OOM | 无用对象未释放(如静态集合缓存、未关闭的连接、ThreadLocal未清理) |
| G1 晋升失败 | 日志含Promotion Failure,触发紧急Full GC | 老年代无连续Region;IHOP设置过低;大对象过多 |
示例:问题分析实战
日志片段:
[10:00:00.000] GC(100) Pause Young: 150ms (Young GC STW超标) [10:00:05.000] GC(101) Pause Young: 140ms (5秒触发一次,频繁) [10:01:00.000] GC(110) Pause Full: 1800ms (Full GC频繁且STW长) [10:01:00.000] Heap after GC: Old Gen: 1900M->1880M (2048M) (GC后占用率92%)分析结论:
- Young GC每5秒一次(频繁),STW 150ms(超标)→ 新生代过小或临时对象过多;
- Full GC 1分钟一次,STW 1.8秒,GC后老年代占用92% → 老年代内存不足,大概率内存泄漏。
三、基于GC日志的性能优化策略
优化原则:先应用层→再JVM参数→最后收集器选型,避免盲目调参。
1. 应用层优化(治本,优先做)
针对“对象创建/释放”的根因优化,从源头减少GC压力:
- 减少临时对象:复用字符串(StringBuilder替代+拼接)、复用集合(线程池/对象池)、避免循环创建对象;
- 排查内存泄漏:用MAT/JProfiler分析堆快照,定位大对象/未释放对象(如静态Map缓存无过期、ThreadLocal未remove);
- 拆分大对象:将超过Region一半的Humongous对象拆分为小对象(G1),避免直接进入老年代;
- 资源及时释放:关闭IO流/数据库连接、清理无用缓存。
2. JVM参数优化(治标,针对性调)
(1)堆内存配置
- 统一Xms/Xmx:避免堆动态扩容触发Full GC(如
-Xms2048M -Xmx2048M); - 调整新生代比例:G1默认新生代占堆40%,Young GC频繁可调大(
-XX:G1NewSizePercent=50);Parallel可调大Xmn(-Xmn1024M,新生代1G); - 老年代预留空间:CMS设置
-XX:CMSInitiatingOccupancyFraction=70(老年代70%触发并发回收);G1调整-XX:G1HeapWastePercent=5(允许5%内存浪费,减少Mixed GC)。
(2)GC线程数
- 匹配CPU核心数:
-XX:ParallelGCThreads=4(4核CPU),避免线程过多导致上下文切换; - G1并发线程数:
-XX:ConcGCThreads=2(并行GC线程数的50%)。
(3)针对STW优化
- G1避免大对象:
-XX:G1HeapRegionSize=16M(调大Region,减少Humongous对象); - CMS减少重新标记耗时:
-XX:+CMSParallelRemarkEnabled(并行重新标记); - 禁用显式GC:
-XX:+DisableExplicitGC(避免代码调用System.gc()触发Full GC)。
3. 收集器选型优化
根据业务场景选择合适的收集器,避免“一刀切”:
| 业务场景 | 推荐收集器 | 避坑点 |
|---|---|---|
| 高吞吐(后台任务/批处理) | Parallel GC | 避免用CMS/G1(并发开销影响吞吐) |
| 低延迟(电商/金融) | G1 GC | 避免用Parallel(STW过长);JDK8需升级到JDK8u20+(修复G1性能问题) |
| 极低延迟(毫秒级) | ZGC/Shenandoah | JDK11+支持,需升级JVM |
四、总结
- GC日志格式:核心是「时间戳+标签+GC事件+内存变化+时长」,不同收集器的日志特征不同,但STW均以
Pause + 时长标识; - 分析步骤:先定基准→统计频次/时长/占用率→结合日志特征定位问题(如频繁Full GC、STW超标);
- 优化优先级:应用层(减少对象创建/排查泄漏)>JVM参数(调堆/线程数)>收集器选型;
- 工具辅助:优先用GCEasy/GCViewer可视化分析,减少手动统计误差。
最终目标:将Young GC频次控制在合理区间,STW时长符合业务阈值,彻底避免非强制Full GC,让GC对业务的影响可忽略。