JVM 知识最终要落到线上排查。供应链系统通常链路长、依赖多、峰值明显,一次订单高峰可能同时触发接口变慢、消息堆积、GC 频繁、CPU 飙高。排查时不能只看某一个指标,而要把 JVM、业务流量、线程、内存和日志串起来。
整体流程图
必须掌握的工具
常用 JVM 排查工具包括:
jps:查看 Java 进程。jstat:查看 GC、类加载、编译等统计。jstack:导出线程栈,定位死锁、阻塞、CPU 高。jmap:查看堆概要、导出 heap dump。jcmd:JDK 7 以后推荐的综合诊断工具。- JFR:低开销运行时事件记录,适合分析 CPU、锁、IO、GC。
- Arthas:线上 Java 诊断工具,适合查看方法调用、耗时、类加载、对象信息。
生产环境使用这些工具要遵守权限和变更规范。导出堆 dump 可能造成磁盘压力,jmap -dump 也可能让进程短暂停顿,必须评估影响。
场景一:Full GC 频繁
假设订单服务在大促期间频繁 Full GC,接口 P99 明显升高。第一步先确认 GC 情况:
1 | jps -l |
重点看 YGC、YGCT、FGC、FGCT、O。如果老年代使用率长期接近 100%,并且 Full GC 后下降不明显,说明很多对象仍然可达。
继续导出堆直方图:
1 | jcmd <pid> GC.class_histogram | head -n 40 |
如果发现大量订单上下文对象:
1 | num #instances #bytes class name |
就要回到业务代码查缓存、静态集合、消息重试队列、批处理暂存区。供应链系统常见原因是把订单处理上下文放入本地缓存,但没有过期时间或清理机制。
1 | public class BadOrderContextCache { |
这类代码应该替换成有容量和过期策略的缓存,或者把上下文持久化到 Redis、数据库,并按业务状态清理。
场景二:OOM
OOM 发生时,先看异常类型:
1 | java.lang.OutOfMemoryError: Java heap space |
建议生产默认开启:
1 | -XX:+HeapDumpOnOutOfMemoryError |
如果是 Java heap space,使用 MAT、VisualVM 或 JProfiler 分析 dump,重点看 Dominator Tree 和最大保留对象。如果是 Metaspace,检查动态类、代理、脚本、热部署。如果是 Direct buffer memory,检查 Netty、NIO、文件导入导出。如果是 unable to create native thread,检查线程池是否无界、容器 pids 限制和 -Xss。
供应链报表导出里的典型 OOM:
1 | public byte[] exportAllOutboundOrders() { |
这个实现同时把全量订单和完整 Excel 放在内存里。更合理的方式是流式查询和流式写出:
1 | public void exportOutboundOrders(OutputStream outputStream) { |
场景三:CPU 飙高
CPU 高时,不能只重启。要先找到高 CPU 线程:
1 | top -Hp <pid> |
如果线程栈显示大量时间消耗在价格计算、促销匹配、库存路由,可以继续结合业务日志和方法耗时定位。常见原因包括死循环、递归层级过深、正则表达式回溯、JSON 大对象序列化、锁竞争导致线程忙等。
例如库存路由中错误的递归:
1 | public WarehouseRoute findRoute(String skuCode, String warehouseCode) { |
如果 fallbackWarehouse() 在某些配置下返回原仓库编码,就会无限递归,最终 CPU 飙高或栈溢出。生产代码要增加访问集合和终止条件。
排查顺序建议
一次线上 JVM 故障可以按这个顺序处理:
- 先止血:限流、摘流量、扩容、暂停批处理,保护核心交易链路。
- 保留现场:保存 GC 日志、业务日志、线程 dump、必要时保存 heap dump。
- 判断类型:CPU、内存、GC、线程、类加载、IO 还是外部依赖。
- 缩小范围:结合发布时间、流量峰值、任务调度、业务操作。
- 验证根因:用 dump、日志、监控和代码路径互相印证。
- 修复并复盘:补监控、补限流、补压测、补容量评估。
JVM 排查能力的本质,是把现象变成证据,把证据映射到代码和业务。对供应链系统来说,订单高峰、仓储波次、库存同步、报表导出都可能触发 JVM 问题,排查时要同时看业务峰值和运行时状态。