JIT逆优化引发的Java服务瞬时抖动 问题排查解决方案

目录

一、背景

二、前期排查(失败)

三、使用神器JFR

四、学习JIT&思考解决方案

五、最终的解决方案

五、总结

一、背景

我们有一个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编译真的会持续这么多天吗?我们并不能理解,开启了JIT编译日志打印也没看出什么。并且JIT参数调优我们也试了,效果也并不明显。

排查陷入了停滞...

三、使用神器JFR

1、JFR的简介与作用

JFR全程是Java Flight Recorder,即Java飞行记录器。借助JFR我们可以把Java服务的各种事件记录下来,如:各种JIT事件的发生时刻、原因等细节;新开线程的时间;各个时间点各线程对CPU的占用情况...这样就可以把服务异常时刻的各种指标记录下来,大大提升服务的可观测性。

详细了解推荐这位大佬的系列博客:Java 监控 JFR

2、JFR常用命令

# JVM参数开启JFR
-XX:StartFlightRecording=filename=/logs/flight.jfr,maxsize=10g
-XX:FlightRecorderOptions=repository=/logs/tmp #指定临时记录的目录
# 检查正在运行的JFR
jcmd JFR.check
# JFR不会自动导出记录,需要通过命令转储
# 转储所有的记录
jcmd <pid> JFR.dump filename=/logs/flight.jfr
# 转储最后n小时的记录
jcmd <pid> JFR.dump begin=-1h
jcmd <pid> JFR.dump maxage=1h
# 转储指定日期
jcmd <pid> JFR.dump begin=2024-01-01T13:00:00 end=2024-01-01T14:00:00 filename=/logs/flight.jfr

3、使用JFR定位问题根因

有了工具的加持,后面的问题排查就顺利了很多。我们很容易就发现了服务的抖动总是伴随着JIT的逆优化、再编译事件,并且逆优化的原因几乎都是C2激进的分支预测发生了失败,逆优化的代码集中在依赖的json库上。

四、学习JIT&思考解决方案

相关资料

JIT分层编译阈值策略

基本功 | Java即时编译器原理解析及实践 - 美团技术团队

(下图来自上文)

思考

  • 由上述资料我们可以得知,JIT的level 4编译发生逆优化后,代码将发生解释运行
  • 此时我们几乎可以猜测抖动就是来自于JIT逆优化后的解释运行(解释运行性能极差),所以解决方案的核心在于避免逆优化
  • level 1编译不会发生逆优化,可以将分层编译固定在level 1,但是性能会比level 4差30%(实测性能发生了不小的下降,方案不够完美,但TP抖动确实消失了)
  • 因为逆优化集中在json库,尝试更换其他json库(失败,没有效果)
  • 修改分层编译的阈值,避免大量方法被level 2、3、4编译(失败,产生了连锁反应,抖动加剧)
  • 再次陷入了僵局...

五、最终的解决方案

山重水复疑无路,柳暗花明又一村。灵光乍现+好运加成,终于被我找到了两个很有效的方案!

1、使用graal编译器

-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompile

压测的效果不错,压测了10小时抖动只发生的1~2次,差不多是原来的1/10。

猜测可能是graal对分支预测相关的逻辑有优化,避免了频繁的逆优化及代码的解释运行。

2、修改OpenJDK源码禁用C2的分支预测

  • openjdk编译流程:Building the JDK

  • openjdk源码下载:GitHub - openjdk/jdk: JDK main-line development https://openjdk.org/projects/jdk

  • openjdk源码修改,注释分支预测逻辑,直接返回PROB_FAIR(Fair probability 50/50,即各有一半的机会):

    //-----------------------------branch_prediction-------------------------------
    float Parse::branch_prediction(float& cnt,BoolTest::mask btest,int target_bci,Node* test) {return PROB_FAIR;// float prob = dynamic_branch_prediction(cnt, btest, test);// // If prob is unknown, switch to static prediction// if (prob != PROB_UNKNOWN)  return prob;// prob = PROB_FAIR;                   // Set default value// if (btest == BoolTest::eq)          // Exactly equal test?//   prob = PROB_STATIC_INFREQUENT;    // Assume its relatively infrequent// else if (btest == BoolTest::ne)//   prob = PROB_STATIC_FREQUENT;      // Assume its relatively frequent// // If this is a conditional test guarding a backwards branch,// // assume its a loop-back edge.  Make it a likely taken branch.// if (target_bci < bci()) {//   if (is_osr_parse()) {    // Could be a hot OSR'd loop; force deopt//     // Since it's an OSR, we probably have profile data, but since//     // branch_prediction returned PROB_UNKNOWN, the counts are too small.//     // Let's make a special check here for completely zero counts.//     ciMethodData* methodData = method()->method_data();//     if (!methodData->is_empty()) {//       ciProfileData* data = methodData->bci_to_data(bci());//       // Only stop for truly zero counts, which mean an unknown part//       // of the OSR-ed method, and we want to deopt to gather more stats.//       // If you have ANY counts, then this loop is simply 'cold' relative//       // to the OSR loop.//       if (data == nullptr ||//           (data->as_BranchData()->taken() +  data->as_BranchData()->not_taken() == 0)) {//         // This is the only way to return PROB_UNKNOWN://         return PROB_UNKNOWN;//       }//     }//   }//   prob = PROB_STATIC_FREQUENT;     // Likely to take backwards branch// }// assert(prob != PROB_UNKNOWN, "must have some guess at this point");// return prob;
    }

    压测的效果极好,抖动几乎完全消失,并且接口的AVG、TP9999指标并未发生明显下降。

六、总结

  1. 可观测性对计算机系统极其重要,良好的可观测性可以大大提高问题排查、性能优化的效率
  2. 工欲善其事,必先利其器。掌握各种性能分析、问题排查、效率提升工具的使用是很有必要的
  3. 先分析清楚问题的根因才可以解决问题,没找到正确方向的努力只会是隔靴搔痒
  4. 阅读第一手的文档资料(当然大都是英文的),才能得到最准确的信息(这里推荐一个浏览器插件“沉浸式翻译”,可以实现中文与原文的对照阅读)
  5. 对于不同的技术积累,解决问题的维度也是不一样的。熟悉底层技术/源码,能做出惊艳的效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/455994.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux实验记录:使用DHCP动态管理主机地址

前言&#xff1a; 本文是一篇关于Linux系统初学者的实验记录。 参考书籍&#xff1a;《Linux就该这么学》 实验环境&#xff1a; VmwareWorkStation 17——虚拟机软件 RedHatEnterpriseLinux[RHEL]8——红帽操作系统 备注&#xff1a; 动态主机配置协议&#xff08;DHCP&…

论文阅读-通过云特征增强的深度学习预测云工作负载转折点

论文名称&#xff1a;Cloud Workload Turning Points Prediction via Cloud Feature-Enhanced Deep Learning 摘要 云工作负载转折点要么是代表工作负载压力的局部峰值点&#xff0c;要么是代表资源浪费的局部谷值点。预测这些关键点对于向系统管理者发出警告、采取预防措施以…

6.0 Zookeeper session 基本原理详解教程

客户端与服务端之间的连接是基于 TCP 长连接&#xff0c;client 端连接 server 端默认的 2181 端口&#xff0c;也就 是 session 会话。 从第一次连接建立开始&#xff0c;客户端开始会话的生命周期&#xff0c;客户端向服务端的ping包请求&#xff0c;每个会话都可以设置一个…

大数据Zookeeper--案例

文章目录 服务器动态上下线监听案例需求需求分析具体实现测试 Zookeeper分布式锁案例原生Zookeeper实现分布式锁Curator框架实现分布式锁 Zookeeper面试重点选举机制生产集群安装多少zk合适zk常用命令 服务器动态上下线监听案例 需求 某分布式系统中&#xff0c;主节点可以有…

五人分糖果

糖果游戏 【题目描述】 某幼儿园里,有5个小朋友编号为1、2、3、4、5,他们按自己的编号顺序围坐在一张圆桌旁。他们身上都有若干个糖果(键盘输入),现在他们做一个分糖果游戏。从1号小朋友开始,将自己的糖果均分三份(如果有多余的糖果,则立即吃掉),自己留一份,其余两份分…

C语言小游戏:贪吃蛇(游戏代码实现)

❀❀❀ 文章由不准备秃的大伟原创 ❀❀❀ ♪♪♪ 若有转载&#xff0c;请联系博主哦~ ♪♪♪ ❤❤❤ 致力学好编程的宝藏博主&#xff0c;代码兴国&#xff01;❤❤❤ 生命不停&#xff0c;学习不止。大家好&#xff0c;我是大伟&#xff0c;今天的话继续给大家实现我们的C语言…

【CSS】css如何实现字体大小小于12px?

【CSS】css如何实现字体大小小于12px? 问题解决方案transform: scale(0.5)&#xff08;常用&#xff09;SVG 矢量图设置text 问题 文字需要显示为12px&#xff0c;但是小于12px的&#xff0c;浏览器是显示不来的 解决方案 transform: scale(0.5)&#xff08;常用&#xff0…

Spring核心基础:全面总结Spring中提供的那些基础工具类!

内容概要 Spring Framework 提供了众多实用的工具类&#xff0c;这些工具类在简化开发流程、提升代码质量和维护性方面发挥了重要作用&#xff0c;以下是部分关键工具类的总结及其使用场景&#xff1a; StringUtils&#xff1a;不仅提供了基础的字符串操作&#xff0c;如拼接…

完蛋,我被offer包围了|秋招自救指南

前言 &#x1f31f; 白泽时隔8年终于记起了b站的密码&#xff0c;这篇文章的视频讲解版已经上传&#xff0c;出镜怪不好意思的&#xff0c;后面写技术文章也会同步用视频的方式讲解&#xff0c;期待您的关注。 公众号&#xff1a;白泽talk&#xff0c;交流群&#xff1a;622383…

「牵手」联合国,看这家企业如何推动厕所可持续发展

作者 | 叶蓁 来源 | 洞见新研社 “没有人是一座孤岛&#xff0c;每个人都是广袤大陆的一部分。”英国诗人的这句话&#xff0c;用来形容当下的消费市场再准确不过。 当前&#xff0c;正是国家增强经济活力、发展内生动力的关键时期。2023年&#xff0c;我国全年GDP增速5.2%&…

电力负荷预测 | 基于TCN的电力负荷预测(Python)———数据预处理

文章目录 效果一览文章概述源码设计参考资料效果一览 文章概述 基于TCN的电力负荷预测(Python) python3.8 keras2.6.0 matplotlib3.5.2 numpy1.19.4 pandas1.4.3 tensorflow==2.6.0

2024年美赛数学建模F题思路分析 - 减少非法野生动物贸易 (1)

# 1 赛题 问题F&#xff1a;减少非法野生动物贸易 非法的野生动物贸易会对我们的环境产生负面影响&#xff0c;并威胁到全球的生物多样性。据估计&#xff0c;它每年涉及高达265亿美元&#xff0c;被认为是全球第四大非法交易。[1]你将开发一个由数据驱动的5年项目&#xff0c…