首页 > 其他分享 >C2 CompilerThread9 长时间占用CPU解决方案

C2 CompilerThread9 长时间占用CPU解决方案

时间:2023-12-06 15:34:50浏览次数:34  
标签:代码 JIT 编译 编译器 CompilerThread9 C2 C1 CPU

一、问题描述
近期在进行日常巡检时发现,线上部分应用服务器的CPU突然比以往高出很多,经过登录机器排查确认是C2 CompilerThread9线程始终长时间运行消耗了CPU。

排查步骤在上篇博文有记录总结,地址:排查CPU异常步骤_u012538947的专栏-CSDN博客_cpu异常

异常线程的堆栈如下:

"C2 CompilerThread9" #48 daemon prio=9 os_prio=0 tid=0x00007f45f0b80000 nid=0x188 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
补充描述:我的应用类型为后台接口服务,系统秒级调用峰值在10W+,JRE版本如下:

java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)
二、问题解决过程
思路1:修改codeCache的默认大小(结论:没有效果)
什么是CodeCache?(主要参考博文:Code Cache满导致应用性能降低_weixin_34203426的博客-CSDN博客)

Java代码在执行次数达到一个阈值会触发JIT编译,一旦代码块被编译成本地机器码,下次执行的时候会直接运行编译后的本地机器码。所以这本地机器码必须被缓存起来,而缓存这个本地机器码的内存区域就是Code Cache,它并不属于Java堆的一部分,除了JIT编译的代码之外,Java所使用的本地方法代码(JNI)也会存在codeCache中。

通过查阅了网上的N多资料,了解到当CodeCache空间使用完之后,JVM的JIT功能会被停止,将不会编译任何额外的代码;被编译过的代码仍然以编译方式执行,但是尚未编译的代码智能以解释的方式执行了。在这种情况下,如果应用中很多代码以解释方式执行,系统性能势必会大大降低,第一感觉貌似就是这个问题,于是开始修改JVM启动参数,验证方案是否可行。

步骤一:通过jinfo命令查看当前默认的CodeCache大小,得到的结论与网上JDK版本对应的默认大小一致,为240M。具体命令如下,pid为Java进程ID。

jinfo -flag ReservedCodeCacheSize pid
步骤二:查看当前CodeCache使用情况,这一步异常坎坷,由于线上堡垒机权限问题,网上的很多办法都没有成功。具体的方式大概有5种,分别如下:

jvm启动参数加上-XX:+PrintCodeCache,可以在jvm关闭时输出code cache的使用情况
使用jcmd的Compiler.codecache,其输出跟-XX:+PrintCodeCache相同;
使用jcmd的VM.native_memory也可以查看code cache的使用情况(Code部分)
使用JMX来获取NON_HEAP类型中的name为CodeHeap开头的MemoryPoolMXBean可以得到code cache的使用情况
如果是springboot应用,它使用micrometer,通过/actuator/metrics接口提供相关指标查询功能,其中code cache在jvm.memory.used这个metric中
具体参考地址:聊聊jvm的Code Cache - 简书

步骤三:设置JVM启动参数,死马当活马医。设置CodeCache大小为300M,具体参数如下:

-XX:ReservedCodeCacheSize=300M
很遗憾,重启后运行一段时间发现,系统CPU负载依然会升高。后来通过公司的统一监控平台证明了我的CodeCache并没有使用满,由于CodeCache是属于堆外内存,然而我的系统堆外缓存使用一共还不到200M,所以也从一方面证明了思路一的方向是错误的。非堆内存使用大小截图如下:

 

 

如果计划尝试思路一的方法来解决问题,建议大家先想办法证明应用的CodeCache确实使用满了,正常来说JDK8 版本的默认值240M已经足够用了,如果其他版本的JRE环境可能确实会有问题。如果无法证明满了,也可以像我一样死马当活马医,试试就知道结果了,但是不要抱太大希望。

思路2:关闭JIT分层编译(结论:有效果)
在继续寻找解决方案的过程中,在网上看到一个案例,和我的JRE环境完全相同,问题现象完全相同,案例中通过关闭JIT分层编译解决了问题,于是我也尝试了这一方案,实践证明问题得到了解决。案例地址:https://community.oracle.com/message/15472264#15472264

步骤一:了解JIT编译原理

因为之前对JIT的编译原理并不了解,不敢随意修改线上服务器的编译类型,担心会有一些其他的副作用,所以在网上开始了查阅资料学习的过程。

什么是JIT编译?

编译器在编译过程中通常会考虑很多因素。比如:汇编指令的顺序。假设我们要将两个寄存器的值进行相加,执行这个操作一般只需要一个CPU周期;但是在相加之前需要将数据从内存读到寄存器中,这个操作是需要多个CPU周期的。编译器一般可以做到,先启动数据加载操作,然后执行其它指令,等数据加载完成后,再执行相加操作。由于解释器在解释执行的过程中,每次只能看到一行代码,所以很难生成上述这样的高效指令序列。而编译器可以事先看到所有代码,因此,一般来说,解释性代码比编译性代码要慢。

java 作为静态语言十分特殊,他需要编译,但并不是在执行之前就编译为本地机器码。Java的实现在解释性和编译性之间进行了折中,Java代码是编译性的,它会被编译成一个平台独立的字节码程序。JVM负责加载、解释、执行这些字节码程序,在这个过程中,还可能会将这些字节码实时编译成目标机器码,以便提升性能。

所以,在谈到 java的编译机制的时候,其实应该按时期,分为两个部分。一个是 javac指令 将java源码变为 java字节码的静态编译过程。 另一个是 java字节码编译为本地机器码的过程,并且因为这个过程是在程序运行时期完成的所以称之为即时编译(JIT:Just In Time)。

JIT编译类型:C1编译器、C2编译器、分层编译器。

通常我们说即时编译器有两种类型,Client Compiler(C1编译器)和Server Compiler(C2编译器)。这两种编译器最大的区别就是,编译代码的时间点不一样。C1编译器会更早的对代码进行编译,因此在程序刚启动的时候,C1编译器比C2编译器执行的更快,所以C1编译器适用于一些GUI应用,可以缩短应用启动时间。C2编译器会收集更多的信息,然后才对代码进行编译优化,所以从长远角度考虑,C2编译器最终可以产生比C1编译器更优秀的代码,适用于长时间运行的后台接口服务。

可能大家都有一个困扰,JVM为什么要将编译器分为client和server,为什么不在程序启动时,使用client编译器,在程序运行一段时间后,自动切换为server编译器? 其实,这种技术是存在的,一般称之为 Tiered Compiler(分层编译器)。Java7 和Java 8可以使用选项-XX:+TieredCompilation来打开(-server选项也要打开)。在Java 8中,-XX:+TieredCompilation默认是打开的。

分层编译将 JVM 的执行状态分为了 5 个层次:

第 0 层:程序解释执行,默认开启性能监控功能(Profiling),如果不开启,可触发第二层编译;

第 1 层:可称为 C1 编译,将字节码编译为本地代码,进行简单、可靠的优化,不开启 Profiling;

第 2 层:也称为 C1 编译,开启 Profiling,仅执行带方法调用次数和循环回边执行次数 profiling 的 C1 编译;

第 3 层:也称为 C1 编译,执行所有带 Profiling 的 C1 编译;

第 4 层:可称为 C2 编译,也是将字节码编译为本地代码,但是会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。
在一些特殊情况下,激进优化后的代码并不能有更高的性能。需要进行优化回退,将重新对代码进行解释执行。
通过以上资料学习,我们得出结论:

C2编译器相对于C1编译器更适用于我们系统
分层编译器是综合考虑C1和C2编译器的优点衍生出的一种进化版本编译器,但是由于我们是纯后台应用,这种衍生优化是否有效未可知。
分层编译器在一些特殊情况下可能比较激进、不可靠。
JIT学习参考博文:

Java性能优化指南系列(三):理解JIT编译器_qq_28674045的博客-CSDN博客_cicompilercount

浅谈对JIT编译器的理解。 - stubbornnnnnnn - 博客园

Java 面试-即时编译( JIT ) - jianjianqq - 博客园

步骤二:关闭分层编译,启用C2编译器

JVM启动脚本中添加如下参数

-XX:-TieredCompilation -server
关闭分层编译后,我们对线上服务器的性能情况进行了监控比较,性能几乎没有什么区别,甚至更优于原来的分层编译(猜测是因为系统CPU负载下降了导致)。性能截图如下(红色曲线为关闭分层编译的机器性能走势图):

 

 

启用分层编译时的CPU负载截图:

 

 

关闭分层编译后的CPU负载截图:

 


————————————————
版权声明:本文为CSDN博主「西兰花不蓝」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012538947/article/details/104601418

标签:代码,JIT,编译,编译器,CompilerThread9,C2,C1,CPU
From: https://www.cnblogs.com/softidea/p/17879662.html

相关文章

  • 客户案例:SMC2威胁感知升级,保障金融行业邮件安全
    客户背景某基金公司是一家在业界享有广泛声誉的综合型资产管理公司,总部位于广州,在北京、上海、香港等地区均设有公司,业务范围遍布全球,凭借其卓越的投资业绩和专业的基金管理服务,赢得了广大投资者的高度认可。该基金公司不仅在专业领域表现出不俗的实力,同时对邮件安全领域也给予了高......
  • cpu占用率很高怎么排查????
    问题显示:cpu太高: 1.top命令查出进程:18720进程cpu最高: 2.查出这个进程是由哪个线程提供的:top-H-p18720  或者 top-Hp18720 ##可以看出18745线程占用cpu最高 3.将线程id转换位16进制:printf'0x%x\n'18745 4.jstack 进程pid|grep 16进制pid -A......
  • [ABC254E] Small d and k 题解
    题目传送门一道暴力题。度数和\(k\)那么小?直接暴力\(n\)遍bfs,注意bfs的队列只能push距离不超过\(3\)的点。但有个问题,每次bfs都需要清空一次距离数组,这样子的时间复杂度是\(O(n^2)\)的。但也不难想到,距离数组中被赋值的地方不会很多,记录一下就行。Code#include......
  • Java获取CPU占用率、内存占用率
    packagetest.lyh.test;importcom.alibaba.fastjson.JSON;importcom.alibaba.fastjson.JSONObject;importjava.lang.management.ManagementFactory;importjava.lang.management.OperatingSystemMXBean;importjava.math.BigDecimal;importjava.math.RoundingMode......
  • 代码随想录算法训练营第5天 | lc242、lc349、lc202、lc1
    (本合集全部为Go语言实现)相关文章链接:242题解349202题解1题解相关视频链接:Leetcode242状态:秒了实现过程中的难点:对于元素固定是小写字母或类似的情况,可以使用数组,因为元素最大数量是固定的个人写法funcisAnagram(sstring,tstring)bool{iflen(s)!=len(t)......
  • 代码随想录算法训练营第4天 | lc24、lc19、lc面试题02.07、lc142
    (本合集全部为Go语言实现)相关文章链接:24题解19题解02.07题解142题解相关视频链接:Leetcode24状态:秒了实现过程中的难点:对组内两个节点的指针指向流转需要倒腾明白。临时头结点真的很有用个人写法funcswapPairs(head*ListNode)*ListNode{tmpHead:=&ListNode{-......
  • Java应用导致CPU使用率过高的排查方法
    1、搭建CentOS7.9,部署JDK8:2、编写测试代码Test.java:publicclassTest{publicstaticvoidmain(String[]args){System.out.println("测试死循环对CPU的影响");while(true){}}}3、编译Test.java:#javacTest.java4、运行Test程序:#jav......
  • ICPC2022Hangzhou C No Bug No Game 题解
    LinkICPC2022HangzhouCNoBugNoGameQuestion给定\(n\)个物品和上限\(k\),要求最大化分数,物品的选择顺序可以任意第\(i\)个物品一行\(p_i\)代表个数,后面\(p_i\)个\(w_j\)代表容量,定义\(sum=\sum\limits_{j=1}^{i-1}\),对于第\(i\)个物品\(sum+p_i\lek\)......
  • ICPC2022Hangzhou A Modulo Ruins the Legend 题解
    LinkICPC2022HangzhouAModuloRuinstheLegendQuestion求$$\sum\limits_{i=1}^na_i+n\timess+\frac{n(n+1)}{2}\timesd\modm$$的最小值Solution我们把这个式子看成一一个二元不定方程\(ax+by+sum\modm\)的最小值,其中\(a=n,b=\frac{n(n+1)}{2},sum=\sum\limits......
  • [题解]AT_abc224_e [ABC224E] Integers on Grid
    比较符合CCF造数据水平的题。思路首先可以用两个vector<pair<int,int>>v[N]分别将每一行、每一列的元素的权值与编号存储下来。那么可以对所有的\(v_i\)按照权值从小到大排序。那么发现对于所有的满足v[i][p].fst<v[i][q].fst的\((p,q)\)都可以建一条从\(p\)指......