ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

凌晨三点的 Full GC:一场与 STW 的生死时速

凌晨三点的 Full GC:一场与 STW 的生死时速 凌晨三点支付系统的监控大屏突然变成一片刺眼的红色。杨工99 线延迟从 50ms 飙到 3 秒了 值班的小刘声音都在抖而且……而且服务好像被注册中心摘除了我冲到工位前扫了一眼监控曲线——Full GC 频率像心跳一样密集每分钟 5 次单次暂停 4.2 秒。别慌 我按住他的肩膀Full GC 是 STWStop-The-World的全局回收。暂停 1 秒支付超时率上升暂停 3 秒连接池耗尽暂停 10 秒服务就被注册中心踢下线了。我们现在就是在和 STW 抢时间。第一幕急诊分诊——jstat 实时监控我先别急着重启重启会丢现场。打开终端执行这个命令jstat -gcutil 12345 1000小刘出来了S0、S1、E、O、M……这些列是什么意思我这是 GC 健康度的心电图。重点看三列OU老年代使用率现在是多少FGCFull GC 次数是不是一直在涨FGCTFull GC 总耗时占应用运行时间的比例超过 10% 就是严重问题。小刘OU……99%而且 FGC 每 12 秒就加 1我老年代已经爆满了。但光知道满了没用得知道为什么满。接下来看 GC 日志。第二幕追根溯源——GC 日志找触发原因我你确认启动参数里配了 GC 日志吗小刘配了JDK 8 的-XX:PrintGCDetails -XX:PrintGCDateStamps -Xloggc:/var/log/app/gc.log我好。JDK 11 的话用-Xlog:gc*:filegc.log。现在去翻日志找最近一次 Full GC 的记录看reason字段写的是什么。小刘飞快地滚动日志突然停住找到了[Full GC (Allocation Failure) ...]我Allocation Failure说明是老年代空间不足触发的。但这只是表象就像发烧是症状不是病因。我们需要区分真正的根因。我把一张根因对照表推到他面前根因特征解法内存泄漏无界缓存、static 集合FGC 后老年代 OU 不回落稳定在 99%dump MAT 定位代码换 Caffeine 限界缓存大对象 / 一次加载过多堆曲线呈锯齿状骤降分页查询、流式处理、对象池化Survivor 区太小对象提前晋升S0/S1 常年接近 100%调-XX:SurvivorRatio、-XX:TargetSurvivorRatio显式System.gc()日志 reason 显示System.gc()排查代码 / 加-XX:DisableExplicitGCMetaspace 满日志 reason 显示metaspace查动态类生成CGLIB、反射调大-XX:MaxMetaspaceSize堆外内存依赖 Full GC 回收堆内存正常但 FGC 频繁限制-XX:MaxDirectMemorySizeNetty 及时release()参数不合理如只设-Xmx没设新生代新生代约为堆的 3/8-XX:SurvivorRatio8小刘我们的 OU 在 FGC 后完全不回落……这应该是内存泄漏我对。走场景二的排查流程dump MAT。第三幕锁定真凶——MAT 分析半小时后dump 文件分析完成。小刘杨工MAT 的 Leak Suspects 报告标红了ConcurrentHashMap$Node[]占了 78% 的 Retained Heap我顺引用链往下追找到业务代码。小刘找到了……是一个交易缓存static final ConcurrentHashMap只 put 不 remove永不过期我经典泄漏模式。交易数据只进不出老年代迟早被吃光。改成 Caffeine设maximumSize(10_000) 5 分钟过期。第四幕亡羊补牢——收集器选型与架构原则修复上线后Full GC 归零99 线回落到 68ms。小刘杨工以后怎么避免再踩这种坑我记住三点收集器选型堆 8G 以下用 Parallel8G32G 用 G1低延迟大堆用 ZGCJDK 17最大暂停毫秒级。架构期预留余量缓存必须设上限、线程池必须用有界队列、内存预留 30% 余量。一句总结Full GC 不是调参调出来的是设计出来的。小刘那为什么不一上来就加堆我如果 OU 在 FGC 后不回落说明是泄漏。加堆只是把死期推迟反而放大单次 GC 停顿。治标不治本。尾声天亮了监控大屏恢复绿色。小刘杨工这次学到的东西比看十篇博客都管用。我记住排查 Full GC 就像急诊抢救先看生命体征jstat再查病因GC 日志最后手术MAT 定位。套路熟了凌晨就不慌了。他点点头补了一句还有以后启动参数我一定检查三遍。我笑了这才是今晚最大的收获。
返回列表