目录
一、背景
我们有一个QPS较高、机器数较多的Java服务;该服务的TP9999一般为几十ms,但偶尔会突然飙升至数秒,并会在几秒内自动恢复(抖动期间伴随着CPU占用100%、线程池大量扩容)。抖动大都集中在新代码上线后的前几天,会随着时间拉长逐渐减少。
二、前期排查(失败)
前期未排查到问题根因,也不知道如何去定位根因;只好从现象出发(CPU占用100% 和 线程池大量扩容),尝试通过解决表面现象,从而避免服务抖动。具体做了以下工作进行测试验证:
工作项 | 预期 | 结果 |
---|---|---|
固定线程池线程数 | 避免因线程创建销毁、线程上下文切换产生的CPU开销 | 抖动时的TP峰值降低,但抖动仍存在 |
监控线程CPU占用的shell脚本 | 捕获异常时刻到CPU占用高的线程 | 捕获到的线程比较多,有业务代码线程、C2编译器线程、GC线程... |
JIT调优(提高编译阈值、减少C2线程数...) | 降低CPU占用 | 效果不明显 |
试用JDK21的虚拟线程 | 避免因线程创建销毁、线程上下文切换产生的CPU开销 | 使用虚拟线程后,抖动时的TP峰值降低,但抖动仍存在 |
试用JDK21的结构化并发 | 避免部分业务线程查存储失败后,其他线程还在运行、持续占用CPU | 结构化并发也是基于虚拟线程的,效果和虚拟线程类似 |
试用分代ZGC | 降低GC线程的CPU占用 | 无效 |
这时候我们发现针对表面的现象可以做的猜想实在是太多了,对应的实验也太多了,很多时候也很难通过实验去完全地证伪这些猜想。
基于“抖动大都集中在新代码上线后的前几天”的现象,服务冷启动和JIT编译确实有很大的嫌疑,但是JIT编译真的会持续这么多天吗?我们并不能理解,开启了J