最近我们项目组那个线上服务的GC问题,搞得我头都大了。经常半夜被电话叫起来,说延时又飙上去了。一看监控,好家伙,百分之九十九的请求都在等GC收尸。这活儿没法干了。
为什么非要找最新版本?我被旧版本坑惨了
我们之前一直用那个圈里人俗称“GC义父”的分析工具,老版本也能凑合用,但自从我们上了最新的JDK 17,那老版本工具就有点跟不上节奏了。它报告里说的问题,我按照调优建议改了,根本没用。我猜它对ZGC和最新的G1优化分析不全。
得找最新版本,这是第一步,也是最重要的一步。我二话不说,直接上GitHub开始翻。这玩意儿在国内流传的资料大多都是好几年前的,版本号乱七八糟。有的公众号文章里写着1.8.2就是最新版,但我一看Release Log,人家早都跑到2.0去了。这帮人真能误导人。
我为啥对版本号这么较真?说起来我当初就是因为用了错的版本,差点被领导当场给批得狗血淋头。那是在前年,项目刚上线,跑着跑着内存就爆了。我拿了一个网上随便找的旧版工具分析,报告写着是“内存泄漏”。我改了半天代码,结果一点用没有。后来有个老前辈偷偷告诉我,那工具对当时的JVM版本有Bug,报的错是假的。
那次我通宵搞了三天,发现根本不是代码问题,而是GC配置里的一个参数写错了。从那以后,我再也不信那些不知道从哪儿传出来的版本号,一定要自己动手,找到源头。
抠细节:从论坛深处挖出真货
这回我学聪明了,不看国内的二手资料。我直接去扒拉老外那边的邮件列表和官方提交记录。这找东西的过程,简直像考古一样。
我先是爬了他们项目的主分支,发现最新的Commit ID确实动得很频繁,版本标签打得特别快。但光看标签不行,因为有些标签是测试版。我得找到那个被维护者确认,并且稳定运行在主流生产环境的那个版本。我把目标锁定在了“2”开头的一系列版本。
我挨个比对:
2.9.1:这是社区里用得最多的,但有些新功能缺失。
2.10.0-BETA:一看就是测试版,不敢用。
2.10.4:这个版本号很少有人提,但我发现这是在一个知名的云计算公司内部邮件组里被反复提及并推荐的。
我决定相信那个邮件组的推荐。我找到对应的Commit ID,把整个源码拉了下来。自己本地编译了一遍。编译过程倒是挺顺利,没出啥幺蛾子。
实战部署:最新版“义父”立功了
我把编译好的最新版分析工具,替换掉我们线上的旧版本。然后把我们那台最能折腾人的核心交易服务给重启了。配置好参数,开始记录日志。为了保险起见,我跑了一个小时的压测,把服务打到满负荷。
等日志出来,我用这个2.10.4版本的“GC义父”跑了一遍分析。结果报告出来,我一看,眼睛都亮了。
它把以前旧版本根本看不出来的“卡顿原因”全都给我揪出来了。什么TLAB不足导致的小停顿,什么新生代晋升老年代的速度太快,导致频繁Full GC,都被它用非常直观的图表给标出来了。以前我们看到的都是模糊的区域,现在它直接给我指出了具体是哪几毫秒在卡。
根据这个新报告的指导,我把GC的几个关键参数重新调整了一遍,主要是针对新生代和老年代的比例进行了大幅度的修改,还对一个线程池的参数做了微调。改完之后,我再让服务跑了一个下午。
结果非常喜人。我们原本动不动就突破300毫秒的P99延时,直接被压到了100毫秒以内。晚上终于不用再被电话叫醒了。
所以说,版本号这个东西,一定要自己去查源头。那些社区里瞎传的,十有八九都是过时的。这回实践记录告诉我们,最新的稳定版本,就是这个不被大家频繁提起的2.10.4。别再用老版本瞎折腾了,效率太低!