系统吞吐量下降若由GC引起,本质是GC时间占比过高挤占业务执行时间;需用jstat -gc <pid> 1000 10量化YGC、YGCT、FGC、GCT四指标,结合GC日志、堆快照与分配追踪定位根因并针对性修复。

系统吞吐量下降如果由 GC 引起,本质是 JVM 花在垃圾回收上的时间占比过高,挤占了业务代码执行时间。关键不在于“有没有 GC”,而在于“GC 吃掉了多少有效运行时间”。排查必须从量化入手,不能靠猜测。
先确认 GC 是否真是瓶颈
打开终端,执行:jstat -gc <pid> 1000 10
每秒输出一行,连续看 10 秒。重点关注四列:
- YGC:Young GC 次数——若每秒 ≥2 次,说明新生代压力过大
- YGCT:Young GC 累计耗时——除以总运行时间,超 5% 就已拖累明显;超 10% 基本就是主因
- FGC:Full GC 次数——每分钟 ≥1 次属紧急,需立即干预
- GCT:YGCT + FGCT 总和——这是真正影响吞吐的“GC 时间占比”分母
例如 5 分钟(300 秒)内 GCT = 24 秒 → 吞吐量仅 92%,低于健康线(95%)。批处理类应用容忍度略高(≤8%),Web API 类则应控制在 ≤3%。
用 GC 日志定位具体问题类型
光看 jstat 不够,得有日志佐证。启动时加参数(JDK 8):-Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M
打开日志后重点扫三类信号:
- 反复出现 Humongous allocation → 大数组直入老年代,引发“边清边满”式 Mixed GC
- Young GC 后 老年代使用量不降反升,单次涨超 50MB → 新生代对象过早晋升
- Allocation Failure 高频出现在某接口调用周期内(如每处理 100 条消息就触发一次)→ 分配行为与业务强耦合
这些不是随机现象,而是可复现的模式。匹配上,就锁定了 GC 异常的根因方向。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
结合堆快照和分配追踪交叉验证
GC 日志指明“哪里不对”,堆快照和分配采样告诉你“谁干的”:
- 执行
jmap -histo:live <pid>,按内存占用排序,看前 5 名是否集中为[B(byte[])、[C(char[])且单个 >1MB - 用 async-profiler 抓 30 秒分配热点:
./profiler.sh -e alloc -d 30 -f alloc.html <pid>,聚焦火焰图中高频分配[B的栈帧 - 用 JFR 录制:
-XX:+FlightRecorder -XX:StartFlightRecording=duration=60s,过滤jdk.ObjectAllocationInNewTLAB事件,按 Stack Trace 分组,排第一的就是源头方法
常见根因包括:Excel 解析时 new byte[8MB]、JSON 批量序列化未流式处理、报表导出缓存整张数据集。
针对性修复与验证要点
修复不是调大堆或换回收器就完事,要打在要害上:
- 对已知大数组分配点,加
-XX:PretenureSizeThreshold=1048576(1MB),让部分中等数组回归新生代,减轻老年代污染 - 把
new byte[N]改成ThreadLocal<byte[]>复用,避免每次分配新空间 - 批量处理场景改用流式 API(如 Jackson 的
JsonParser、Apache POI 的 SXSSF),而非一次性加载全量 - 验证时别只看单次 GC 耗时,重点对比修复前后 GCT 占比 和 吞吐量指标(如 QPS 或处理速率) 是否回升
不复杂但容易忽略:GC 日志本身若写到慢盘或没轮转,日志 I/O 可能反成瓶颈;-Xmx 设太高导致系统 swap,也会让吞吐断崖下跌。调参前先看资源水位。

















