关键在于识别gc日志格式、筛选高停顿事件、关联内存变化趋势;openjdk 8+默认开启-xloggc后,可用shell工具快速提取>200ms停顿、统计类型、追踪老年代增长并衔接jstat/hprof定位根因。

直接提取Java日志中GC停顿时间并定位内存溢出,关键不是“找关键词”,而是识别GC日志格式、筛选高停顿事件、关联内存变化趋势。OpenJDK 8+ 默认开启 -Xloggc 后的日志结构清晰,配合简单 Shell 工具就能快速定位问题。
确认日志是否含详细GC停顿信息
先检查日志是否启用详细 GC 日志(如 G1 或 ZGC),否则无法提取停顿数据:
- 常见有效日志行示例:
[2024-05-12T10:23:41.123+0800][info][gc] GC(123) Pause Young (Normal) (G1 Evacuation Pause) 245M->120M(512M) 42.8ms - 若日志只有
Full GC或GC pause无毫秒数和内存变化,需先调整 JVM 参数:-Xlog:gc*:file=/path/to/gc.log:time,uptime,level,tags -XX:+UseG1GC - 用
head -n 20 gc.log | grep -i "pause|ms"快速验证是否存在带毫秒的停顿记录
用 awk 提取高频长停顿(>200ms)及对应内存状态
停顿超过 200ms 通常已影响服务响应,需优先关注;同时提取前后内存变化,判断是否伴随老年代持续增长:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 提取停顿 >200ms 的完整行,并打印时间、停顿时长、堆内存变化:
awk '/Pause.*[0-9]+.?[0-9]*ms$/ {match($0, /([0-9]+.?[0-9]*)ms/); ms = substr($0, RSTART, RLENGTH-2); if (ms > 200) print $1,$2,"|",ms"ms","|",$0}' gc.log | head -30 - 统计各类型停顿次数与平均时长:
awk '/Pause/ {match($0, /Pause ([^ ]+)/); type=substr($0,RSTART+6,RLENGTH-6); match($0, /([0-9]+.?[0-9]*)ms/); ms=substr($0,RSTART,RLENGTH-2); cnt[type]++; sum[type]+=ms} END {for (t in cnt) printf "%-15s %4d times, avg %.1fms ", t, cnt[t], sum[t]/cnt[t]}' gc.log
关联老年代使用率趋势,判断是否内存泄漏
单次长停顿可能是偶然,但老年代(Old Gen)使用率持续上升+频繁 Full GC/并发失败,则极可能内存泄漏:
- 提取每次 GC 后老年代占用(G1 中为“Old Space”或“Old Gen”,CMS 中看“ParNew”后紧随的“tenured”):
awk '/GC.*->.*(/ && /M)/ {for(i=1;i[0-9]+M/) {split($(i+1),a,"\("); split(a[1],b,"M"); if (b[1] > 1000) print $1,$2,b[1]"M"} }' gc.log | tail -20 - 生成简易趋势(每百行统计一次老年代峰值):
awk '/->.*M)/ {match($0, /->[0-9]+M/); s=RSTART; e=RSTART+RLENGTH-1; mem=substr($0,s+2,e-s-2); if (mem > max) max=mem} NR%100==0 {print NR, max "M"; max=0}' gc.log
快速定位可疑对象:结合 jstat 或 hprof(非纯 Shell,但可衔接)
Shell 能发现现象,但根因需进一步验证。可在发现异常时段,用 jstat 抓快照或导出堆快照:
- 查当前堆分布(每 2 秒一次,持续 10 次):
jstat -gc $(pgrep -f "java.*Application") 2000 10 - 触发堆转储(需提前加
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/),再用ls -lt /tmp/*.hprof | head -3找最新 dump - Shell 中快速估算 dump 大小与时间关系:
stat -c "%y %s" /tmp/java_pid*.hprof 2>/dev/null | sort -k1
不复杂但容易忽略:GC 日志里真正危险的不是单次 500ms 停顿,而是连续多次 100–300ms 的 Young GC,且每次晋升到老年代的数据量递增——这说明对象生命周期变长或缓存未释放。用 Shell 抓出这些模式,比等 OOM 再分析高效得多。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










