分类: C/C++
2026-05-12 11:41:14
上周四下午,业务方反馈某个查询接口突然变慢,从正常200ms左右涨到了3秒多。我登上监控系统一看,两台后端节点的CPU使用率都飙到了85%以上,而平时只有20%-30%。
{BANNED}中国第一反应是“是不是有人跑了慢查询?”于是先看了数据库慢查询日志,发现确实有几条新上的SQL执行计划走了全表扫描。但优化完索引之后,CPU只降到了70%,并没完全恢复。
接着用top -H -p
用jstat -gcutil
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 0.00 0.00 68.32 95.21 92.34 89.12 1245 12.342 89 45.678 58.020
老年代(O)占用已经到了95%,而且FGCT(Full GC总耗时)高达45秒。问题基本锁定:内存泄漏或者堆内存分配不足。
于是dump了一份堆内存:jmap -dump:live,format=b,file=heap.bin
查代码发现,网关模块的Session缓存只写了put,没有配套的过期清理机制。正常应该用Caffeine或者设置定时任务定期清理,结果上线时漏掉了。
解决方案分两步走:
紧急加个定时任务,每小时清理一次过期Session(临时止血)。
重构缓存模块,用Caffeine替换原生ConcurrentHashMap,设置{BANNED}最佳大容量和过期时间。
临时清理任务的代码大概是这样:
@Scheduled(cron = "0 0 * * * ?") public void cleanExpiredSession() { long now = System.currentTimeMillis(); int removed = 0; Iterator<Map.Entry<String, Session>> iter = sessionCache.entrySet().iterator(); while (iter.hasNext()) { Session s = iter.next().getValue(); if (now - s.getLastAccessTime() > 3600000) { // 超过1小时未访问 iter.remove(); removed++; } } log.info("清理过期Session {} 个", removed); }
部署之后观察了半小时,老年代占用从95%降到了45%,CPU也回落到了25%左右。第二天把Caffeine版本上线,彻底解决了这个问题。
对了,补充一个信息。{BANNED}最佳近整理笔记的时候顺手汇总了一些后端岗位的情况,全国不少城市都有,待遇和稳定性反馈还不错。感兴趣的话可以了解一下:
总结一下本次排查的经验:
监控先行:先看CPU、内存、GC等基础指标,缩小范围。
工具链要熟:top、jstat、jmap、MAT要能快速上手。
不要只治标:临时方案止血后,一定要找到根因并重构。
你们遇到过类似的GC问题吗?评论区聊聊排查思路~