news 2026/6/22 2:30:45

生产环境 JVM 频繁 Full GC 导致接口雪崩,我用这套诊断流程 15 分钟找回凶手

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
生产环境 JVM 频繁 Full GC 导致接口雪崩,我用这套诊断流程 15 分钟找回凶手

生产环境 JVM 频繁 Full GC 导致接口雪崩,我用这套诊断流程 15 分钟找回凶手

凌晨两点半,手机炸了。

监控群连刷了十几条告警:P99 响应时间飙到 8 秒,错误率突破 15%,服务健康检查大面积失败。我爬起来连上 VPN,心里已经猜了个七七八八——这熟悉的节奏,大概率是 GC 出了问题。

果然,打开 Grafana 一看,Full GC 频率从平时的每小时 1-2 次,飙到了每分钟 8-10 次。每次 STW 接近 3 秒,接口不超时才怪。

第一步:先确认是不是 GC 的锅

很多人一上来就翻代码,其实应该先拿数据说话。我直接连上目标机器,用jstat看一眼 GC 概况:

jstat-gcutil<pid>100010

输出是这样的:

S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 0.00 0.00 34.25 98.71 92.34 84.56 1254 12.345 892 2345.678 2358.023

老年代(O 列)使用率 98.71%,Full GC 次数 892 次,累计 STW 时间 2345 秒——将近 40 分钟。这已经不是调优能解决的问题了,老年代基本被塞满了,GC 根本回收不动。

结论:不是 GC 策略问题,是内存里有东西在疯狂占用老年代,而且无法被回收。

第二步:抓内存快照,看谁在吃内存

既然老年代快爆了,直接 dump 内存分析。先确认机器磁盘够,然后:

jmap-dump:format=b,file=/tmp/heap_dump.hprof<pid>

文件下来之后,我用 MAT(Eclipse Memory Analyzer)打开,直接跑Dominator Tree分析。结果一目了然:

Class Name | Shallow Heap | Retained Heap ------------------------------------------------|--------------|-------------- java.util.HashMap$Node[] | 1,024,000 | 1,847,293,456 com.example.order.service.OrderCache$CacheEntry | 320,000 | 1,845,678,234

一个HashMap占用了将近 1.8GB 内存,里面全是OrderCache的缓存条目。再点进去看引用链,发现是个本地缓存——用HashMap手写的,没有设置过期策略,也没有大小上限。

元凶找到了。

第三步:定位代码,确认根因

顺着 Dominator Tree 的引用链,定位到这段代码:

@ComponentpublicclassOrderCache{// 致命问题:没有上限、没有过期、没有清理privatestaticfinalMap<String,CacheEntry>CACHE=newHashMap<>();publicOrderDTOget(StringorderId){CacheEntryentry=CACHE.get(orderId);if(entry!=null){returnentry.getData();}OrderDTOdto=orderService.queryFromDB(orderId);CACHE.put(orderId,newCacheEntry(dto,System.currentTimeMillis()));returndto;}}

问题很明显:

  • 没有容量上限,来一个订单就塞一条
  • 没有过期时间,数据永远留在内存里
  • 没有淘汰策略,老数据一直占着坑
  • 更坑的是,这个服务跑了两周,缓存了将近 200 万个订单对象

业务上这个缓存是想减少数据库查询,但实现方式太粗暴了。平时量小的时候没事,一旦赶上促销或者批量补单,内存直接被打爆。

第四步:修复 + 临时止血

先止血,不能让服务继续崩。我有两个选择:

  1. 重启服务——快,但会丢缓存,且问题还会再犯
  2. 动态清理缓存——更安全

我选了方案 2,用 Arthas 现场清掉缓存:

# 连上 Arthasjava-jararthas-boot.jar<pid># 清空缓存ognl'@com.example.order.service.OrderCache@CACHE.clear()'

缓存清掉之后,老年代使用率立刻从 98% 降到了 35%,Full GC 停止,接口响应恢复正常。整个过程不到 5 分钟。

然后修复代码,换成 Caffeine,加上容量和过期限制:

@ComponentpublicclassOrderCache{privatefinalCache<String,OrderDTO>cache=Caffeine.newBuilder().maximumSize(10000)// 最多 1 万条.expireAfterWrite(10,TimeUnit.MINUTES)// 10 分钟过期.recordStats()// 方便监控.build();publicOrderDTOget(StringorderId){returncache.get(orderId,id->orderService.queryFromDB(id));}}

第五步:JVM 参数优化(锦上添花)

代码修复后,顺便调整了 GC 参数。原来的参数是默认的 Parallel GC,STW 时间比较长。考虑到这个服务对延迟敏感,我换成了 G1,并加了几个关键参数:

-XX:+UseG1GC-XX:MaxGCPauseMillis=200-XX:InitiatingHeapOccupancyPercent=45-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/var/log/heap-dumps/-XX:+PrintGCDetails-XX:+PrintGCDateStamps-Xloggc:/var/log/gc.log

G1 的预测停顿时间配合 IHOP 调整,能在内存压力上来时提前触发并发标记,避免老年代突然被打满。

踩坑记录

1. 不要自己造缓存轮子

这次事故的根本原因是手写了无界HashMap当缓存。Guava Cache 或者 Caffeine 都有完善的容量控制、过期策略和统计监控,比自己写靠谱 100 倍。

2. 监控要前置,别等告警了才看

我们后来给这个服务补了两块监控:

# JVM 内存使用率jvm_memory_used_bytes / jvm_memory_max_bytes# GC 停顿时间(通过 Micrometer + Prometheus)jvm_gc_pause_seconds_max

阈值设成:老年代使用率 > 80% 或者 Full GC 间隔 < 5 分钟就告警。这样下次有问题,能在雪崩之前发现。

3. MAT 的 Dominator Tree 比 Histogram 更直观

一开始我用 Histogram 看,只看到byte[]char[]占了很多内存,但不知道是谁在引用。Dominator Tree 直接展示了引用链,省了不少时间。

4. Arthas 是线上排查神器

这次如果不是 Arthas 能动态清缓存,只能重启服务,会丢失很多数据。建议每个 Java 服务都部署 Arthas,关键时刻能救命。

写在最后

从告警到定位,总共 15 分钟。其中 10 分钟在等 heap dump 下载,真正分析只用了 5 分钟。

这件事给我的教训是:内存泄漏不会立刻爆炸,它会在某个业务量突增的凌晨给你致命一击。平时多关注老年代增长趋势,比事后排查重要得多。

如果你也维护 Java 服务,建议现在就检查一遍——有没有手写 Map 当缓存的?有没有大对象没及时释放的?有没有 GC 日志但没看过的?

预防永远比救火便宜。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/22 2:29:26

SYCL异构编程实战:内存模型、并行抽象与跨平台性能深度解析

1. 项目概述&#xff1a;为什么我们需要重新审视SYCL&#xff1f;如果你在异构计算领域摸爬滚打过几年&#xff0c;大概率会对“一次编写&#xff0c;随处运行”的愿景又爱又恨。爱的是它描绘的美好蓝图——摆脱为每个硬件平台&#xff08;CPU、GPU、FPGA、AI加速器&#xff09…

作者头像 李华
网站建设 2026/6/22 2:23:00

NXP LPC31xx LCD接口编程实战:从6800/8080协议到DMA优化

1. 项目概述与核心价值在嵌入式设备开发中&#xff0c;无论是智能家居的控制面板、工业现场的人机界面&#xff08;HMI&#xff09;&#xff0c;还是便携式医疗仪器的显示屏&#xff0c;稳定、高效的显示驱动都是产品成功的关键一环。而连接微控制器&#xff08;MCU&#xff09…

作者头像 李华
网站建设 2026/6/22 2:19:07

DPDSyn:任务导向的差分隐私数据合成技术原理与实践

1. 项目缘起&#xff1a;当数据合成遇上“既要又要”的困境在数据驱动的时代&#xff0c;我们常常面临一个两难选择&#xff1a;一方面&#xff0c;我们需要利用数据来训练模型、优化业务&#xff0c;数据越多、越真实&#xff0c;效果通常越好&#xff1b;另一方面&#xff0c…

作者头像 李华
网站建设 2026/6/22 2:06:38

免环境配置 OpenClaw 2.7.9 一键搭建 AI 自动化工具

&#x1f539; 一、前言 OpenClaw 是一款口碑出众的高效本地 AI 自动化工具&#xff0c;支持完全离线运行&#xff0c;既不需要连接外网&#xff0c;也不用绑定云端账号&#xff0c;依靠内置的智能 AI 逻辑就能自主完成各类电脑操作。迭代至 v2.7.9 版本后&#xff0c;程序已内…

作者头像 李华
网站建设 2026/6/22 2:04:43

基于击键动力学的USB HID注入攻击检测:从原理到工程实践

1. 项目概述&#xff1a;当你的键盘“背叛”了你想象一下这个场景&#xff1a;你正坐在工位上&#xff0c;手指在键盘上飞舞&#xff0c;处理着一份机密文件。你对自己的电脑安全充满信心——最新的杀毒软件、复杂的防火墙、定期的系统更新。然而&#xff0c;就在你眼皮底下&am…

作者头像 李华