Skip to content

GCViewer 使用教程:离线 GC 日志分析与性能调优 ​

GCViewer 是 JVM 圈子里最老牌的 GC 日志可视化工具:一个 jar、纯 Java 桌面应用、LGPL 开源,双击就能看堆的锯齿图和几十项统计数据,日志全程不出本机。

它和 EasyGC 读的是同一份日志,区别在于形态:EasyGC 在浏览器里跑(免安装、带泄漏检测与调优建议引擎),GCViewer 更适合日志不能外传的内网环境、需要批量/CI 出结构化数据,或者想逐条翻原始 GC 事件的场合。两者结论可以互相验证。

一、安装与启动 ​

当前版本 1.37(2026-05-25 发布):

获取方式地址
可执行 jarhttps://sourceforge.net/projects/gcviewer/files/gcviewer-1.37.jar/download
macOS 应用包gcviewer-1.37-dist-mac.zip(同一个 SourceForge 目录)
Maven Centralcom.github.chewiebug:gcviewer:1.37

第一个坑

GitHub Release 页面里没有附件,只有 SourceForge 的下载链接。找不到 jar 别以为项目没发版。

启动 GUI 需要一个 Java 8 及以上的虚拟机:

bash
java -jar gcviewer-1.37.jar            # 打开 GUI
java -jar gcviewer-1.37.jar gc.log     # 打开 GUI 并直接加载这份日志

从源码构建(需要 Maven 3,产物在 target/):

bash
git clone https://github.com/chewiebug/GCViewer.git
cd GCViewer && mvn clean install

要在自己的工具里复用它的解析器,直接引 Maven Central 坐标即可:

xml
<dependency>
  <groupId>com.github.chewiebug</groupId>
  <artifactId>gcviewer</artifactId>
  <version>1.37</version>
</dependency>

二、先准备一份能解析的日志 ​

GCViewer 的解析能力取决于日志开关给了多少信息。按 JDK 版本对号入座:

JDK推荐参数说明
Java 9+(统一日志)-Xlog:gc=info:file="gc.log":tags,uptime,level官方给出的最小可用配置
Java 9+(推荐)-Xlog:gc*:file=gc.log:tags,time,uptime,level信息最全,含各阶段与并发周期
Java 9+(最大)-Xlog:gc*=trace:file="gc.log":tags,time,uptime,level多余 tag 会被忽略
Oracle JDK 6 / 7 / 8-Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps官方点名的最佳实践组合
Sun JDK 1.4 / 1.5-Xloggc:gc.log -XX:+PrintGCDetails
Sun JDK 1.2.2 / 1.3.1-verbose:gc无时间戳,算不出 Total Time / Throughput
IBM JDK 1.2.2 – 1.4.2-verbose:gc需保证事件没有换行
HP-UX JDK 1.2 – 1.4-Xverbosegc
BEA JRockit 1.4.2 – 1.6-verbose:memory -Xverbose:gcpause,gcreport

两个额外的开关,会直接决定你能看到多少指标:

  • -XX:+PrintGCDetails —— 没有它,年轻代/老年代区域图、Promotion(晋升量)、InitiatingOccFraction 全部缺失。Java 9+ 的 -Xlog:gc* 已等价包含。
  • -XX:+PrintGCApplicationStoppedTime —— 有了它才能看到 VM operations overhead,即安全点里不属于 GC的那部分停顿(偏向锁撤销、线程转储、类重定义等)。

版本差异

1.36 及更早版本里,统一日志(-Xlog)多余的 decoration 会导致解析直接失败;1.37 起已支持全部 decoration。如果你用的老版本报解析错误,先把 decoration 收敛到 tags,time,uptime,level 再试。

三、GUI 怎么看 ​

打开日志 ​

File → Open File 打开单文件;开了 -XX:+UseGCLogFileRotation 的轮转日志用 File → Open Series,把 gc.log.0…gc.log.N 当一个连续日志读;File → Open URL 甚至可以直接喂一个 URL。

压测时两个很实用的开关:

  • File → Watch —— 文件一变自动重载,边压边看;
  • View → show datestamps —— 在"日志开始后的秒数"与"绝对时间戳"之间切换。

图表区(Chart) ​

图例含义能读出什么
Total Heap(红线)已分配的堆总量堆是否被撑大、有没有缩容
Used Heap(蓝线)实际占用锯齿 = 健康;GC 后谷底逐级抬升 = 内存泄漏
Young / Tenured Generation(橙 / 洋红区域)分代占用(需 PrintGCDetails)老年代水位离上限还有多远
Used young / used tenured heap分代实际占用存活对象主要集中在哪一代
Full GC Lines(黑竖线)每次 Full GC密度 = Full GC 频率
Inc GC Lines(青竖线)每次增量 GC
GC Times Line(绿线)停顿时间趋势停顿是在变长还是稳定
GC Times Rectangles(矩形)单次停顿时长,颜色区分 Full GC / initial-mark / remark / VM operation / 普通 GC / 增量 GC一眼看出长停顿来自哪一类事件
Initial mark level(黄线)并发周期启动时的水位(CMS / G1)触发时机是否太晚
Concurrent collections(青竖线开始 / 粉竖线结束)并发回收周期并发周期是否跑不完就又被触发

View 菜单可以逐个开关上面这些图层;1.37 起支持滚轮缩放;在时间标尺上右键可以手工指定日志的绝对开始时间(日志带了 PrintGCDateStamps 时会自动带出)。

事件明细(Event details) ​

按事件类型分页:

  • Gc pauses —— 非 Full GC 的 stop-the-world 停顿;
  • Full gc pauses —— 跨代回收或名字里带 full gc 的停顿;
  • VM operations overhead (safepoint pauses) —— 安全点相对 GC 的额外开销(需 PrintGCApplicationStoppedTime);
  • Concurrent GCs —— 并发周期信息(应用线程仍在跑的那部分)。

先看 Parser 标签页

旁边还有个 Parser 标签,列出解析过程中的警告。有报错就说明有行没吃进去,后面的统计是不完整的——先处理它再看指标。

数据面板(Data Panel,View → Data Panel) ​

三个标签页,下面这些是排查时真正会用到的:

Summary

指标含义关注点
Footprint窗口内堆的最大占用这台机器至少得给这么多内存
Total Time日志覆盖时长太短(几分钟)结论不可信
Throughput业务线程时间占比 = 1 − 累计停顿 / 总时长> 95% 健康,< 90% 要调
Accumulated pauses / GC / full GC各类停顿累计Full GC 那一项应该接近 0
Freed Memory / Freed by GC / Freed by full GC回收掉的字节数
Freed Mem/Min每分钟回收量粗略的分配速率
GC Performance / Full GC Performance单位时间回收量回收效率高不高
Slope GC / Slope full GCGC 后堆占用的增长斜率持续为正 = 泄漏信号
Avg rel inc after GC / after FGC每次 GC 后堆的相对增幅同上,另一个角度

Memory

指标含义
Total / Tenured / Young / Perm heap (usage / alloc. max)各代使用量与分配上限
Avg / Max heap after conc GC并发周期后的堆占用(决定 Footprint 的下限)
Avg / Max heap after full GCFull GC 后的堆占用 —— 逼近 Footprint 就是容量不足
Avg / Max tenured after conc GC老年代水位
Avg / Total promotion晋升量(需 PrintGCDetails)—— 每次 Young GC 有多少对象进了老年代
InitiatingOccFraction (avg / max)并发周期触发时的老年代占用比例(CMS 调 -XX:CMSInitiatingOccupancyFraction 的依据)

Pause

指标含义
Number of pauses / gc pauses / full gc pauses停顿次数
Accumulated / Avg / Min / Max Pause累计 / 平均 / 最小 / 最大停顿
Avg pause interval / Min / max pause interval停顿间隔 —— 比次数更能反映频率与业务节奏
Full gc pauses 同名一组Full GC 单独的统计(只有它没有 avg interval 之外的额外含义,看次数即可)

数字怎么读

Max Pause 直接对 SLO(比如"P99 < 200 ms");Avg pause interval 异常小说明 GC 密集;Total pause 占 Total Time 的比例就是吞吐量的反面。

四、命令行:批量与 CI ​

不带参数(或只带日志)时启动 GUI,参数 ≥ 2 个就进入命令行模式:

bash
# 只导出报告(默认 -t SUMMARY)
java -jar gcviewer-1.37.jar gc.log summary.csv

# 报告 + 堆图 PNG
java -jar gcviewer-1.37.jar gc.log summary.csv chart.png

# 指定导出类型
java -jar gcviewer-1.37.jar gc.log summary.csv chart.png -t CSV_TS

# 轮转日志:用分号串起来当一个连续日志
java -jar gcviewer-1.37.jar "gc.log.0;gc.log.1;gc.log.2;gc.log.current" summary.csv

参数含义:

位置参数必填说明
1<gc-log-file|url>是单个日志,或分号分隔的轮转日志序列,也可以是一个 URL
2<export.csv>是报告输出路径
3[chart.png]否同时渲染一张堆图
—-t <类型>否导出类型,默认 SUMMARY

导出类型怎么选:

类型输出适合干什么
SUMMARY完整摘要(= GUI 数据面板的全部字段)人工归档、发邮件
CSV逗号分隔,聚合后的数据丢进 Excel
CSV_TS每个 GC 事件一行,带 unix 时间戳入库、画趋势、前后两次调优做 diff
PLAIN纯文本(Sun/Oracle 日志兼容 HPjmeter)喂给 HPjmeter
SIMPLE<事件名> <日志开始后秒数> <停顿秒数>喂给 gchisto

命令行的退出码(来自源码常量),CI 里可以直接判断:

退出码含义
0成功
-1导出失败
-2参数解析失败(会打印 usage)
-3位置参数超过 3 个(会打印 usage)

批量跑一整夜的日志:

bash
for f in /var/log/app/gc.log.*; do
  java -jar gcviewer-1.37.jar "$f" "${f}.csv" "${f}.png" -t CSV_TS || echo "FAILED: $f"
done

shell 转义

分号是日志序列的分隔符,在 bash 里必须加引号,否则 shell 会把它当成命令结束。

五、性能优化实战:四类问题的定位路径 ​

拿到一份日志,按下面四条路径走,比"把所有数字看一遍"高效得多。

1. 吞吐量低(Throughput < 95%) ​

看 Summary 的 Throughput 与 Accumulated pauses → 翻 Pause 页,看 Number of gc pauses 和 Avg pause interval。

  • 次数多、间隔小、单次不长 → 年轻代太小,对象刚分配就被回收:调大 -Xmn 或降低 -XX:NewRatio;
  • 单次就很长 → 属于第 3 类问题。

复测目标:Throughput 上升、Avg pause interval 变大(GC 变稀)。

2. Full GC 频繁 / 老年代撑满 ​

看图表里 Full GC Lines(黑竖线) 的密度 → Memory 页对比 Max heap after full GC 与 Footprint:

  • 两者接近 → 容量不足:调大 -Xmx,并审视缓存与数据加载量;
  • 两者差很远却频繁 Full GC → 多半是 System.gc() 或元空间触发,去 Event details 看具体原因;
  • Used Heap 蓝线谷底逐级抬升、Summary 里 Slope GC 为正 → 内存泄漏:GCViewer 到此为止,转 jmap + MAT 找对象(见JVM 排查工具箱)。

3. 单次停顿超过 SLO ​

看 Pause 页的 Max Pause → 回图表看 GC Times Rectangles 的颜色,定位长停顿的类型:

  • 黑色(Full GC)→ 回到第 2 类;
  • remark / initial-mark(CMS)→ 并发周期启动太晚,调低 -XX:CMSInitiatingOccupancyFraction,或换 G1;
  • G1 且停顿分布长尾 → 设 -XX:MaxGCPauseMillis,检查是否有巨型对象(调大 -XX:G1HeapRegionSize);
  • 对延迟极敏感 → 上 ZGC / Shenandoah(1.37 对这两者的解析已增强)。

4. 晋升压力:老年代悄悄变胖 ​

看 Memory 页的 Avg / Total promotion 和 Max tenured after conc GC:

  • 晋升量高 + 老年代持续上涨 → 年轻代太小导致对象"早晋升",或 Survivor 装不下:调大年轻代、调 -XX:SurvivorRatio、提高 -XX:MaxTenuringThreshold;
  • 晋升量大但老年代稳 → 正常,观察即可。

5. 一个容易误判的情况 ​

Event details 里 VM operations overhead 明显高于 GC pauses 时,问题根本不在 GC——是安全点上的其他操作(偏向锁撤销、线程转储、类重定义)。这时候调堆参数是白费力气。

六、GCViewer 与 EasyGC 怎么选 ​

维度GCViewerEasyGC
形态桌面 jar,需 Java 8+网页,打开即用
日志去向本地本地(浏览器内解析,不上传)
图表堆/分代/停顿矩形,可滚轮缩放、图层开关使用率锯齿、停顿分布、触发原因环形图
事件明细逐条 GC 事件 + Parser 警告完整事件明细表
泄漏检测看 Slope / 曲线谷底(人工判断)线性回归自动判定并给出结论
调优建议无15 条规则、四级严重度
批量 / CI命令行,可脚本化手动上传
结构化导出CSV / CSV_TS / PLAIN / SIMPLE / PNGPDF / Word / JSON
部署到内网拷 jar 即可静态站点,也可内网部署

推荐组合:先用 EasyGC 快速看结论和建议 → 需要内网、批量或原始事件时用 GCViewer 复核。两者基于同一份日志,结论一致时你对判断的信心会高很多。

七、常见坑 ​

  • 找不到下载包:jar 在 SourceForge,不在 GitHub Release 附件里。
  • 老版本解析统一日志失败:多余的 decoration 在 1.36 及更早会报错,升到 1.37 或精简 decoration。
  • CMS / G1 并发事件与 STW 混在一起输出:官方 README 明确说解析器有时无法恢复并报错——这是已知限制,换一份日志或改用 -Xlog:gc* 的完整输出。
  • 大日志 OOM:GCViewer 本身也是个 Java 程序,几百 MB 的日志记得 java -Xmx2g -jar gcviewer.jar。
  • 轮转日志:命令行用分号连(记得加引号),GUI 用 File → Open Series。
  • 指标缺一块:没有 PrintGCDetails 就没有晋升量和分代图;没有 PrintGCApplicationStoppedTime 就没有安全点开销。
  • 别忽略 Parser 警告:有未解析行时,所有统计都是"部分数据"的结论。

相关文章 ​