介绍
关于java进程占用cpu问题,一般都是想知道
哪个(些)线程使用了更多的cpu
哪个(些)方法使用了更多的cpu
本文给出一个非常简要的介绍并给出了各个方法的使用例子,更深入使用可参考相关文档。 我觉得这些方法简单实用,已经帮助我解决过几次问题(产品环境过慢,性能调优),希望对你也有帮助。
简要总结
目标
解决方式
说明
适用于
哪个(些)线程占用了很多的cpu
Step 1. 通过ps命令得到jvm的各个线程的cpu使用率(并排序)
Step 2. 通过jstack的输出,找到这个线程(Step1中各个线程都有线程号)
注意:Step1(ps)中线程号为10进制,Step2(jstack)中为16进制。人肉转换的话,可以使用在线工具 http://www.binaryhexconverter.com/hex-to-decimal-converter, 或者 printf "%x\n" decimalNumber 来获得16进制
产品环境
开发/测试环境
哪个(些)方法占用了很多的cpu - via jvisualvm
visualvm连接java 进程后
1. 点击Sample 标签
2. 开始你的测试,然后马上点击cpu按钮
3. cpu占用测试结束后,点击 Stop
4. 点击Snapshot暂存结果
5. 观察分析Snapshot结果
6. Snapshot结果可以保存
下面例子中,提供了截屏。使用方式比较简单直接。
另外:visualvm还支持使用类似的方式查看内存在检测期间的分配情况
ONLY 开发/测试环境
因为一般不允许通过visualvm连接产品环境。
哪个(些)方法占用了很多的cpu - via hprof
添加-agentlib:hprof=cpu=samples到你的java进程启动脚本中,开始测试。测试结束后,关闭java进程。java进程退出后,在当前目录形成一个文件java.hprof.txt。这个文件中,包含了cpu使用方法排名。
本文会提供一个例子输出供参考。
该输出并没有visualvm信息丰富、直观。只有在visualvm无法使用,或者必须background执行的时候,才推荐使用。
None
平时工作中,我几乎从来没用使用这种方法。因为开发环境中,visualvm更合适。生产环境中,hprof是不会被使用的。
这里列出来只是提供一个新的思路。
note: https://www.tablesgenerator.com/markdown_tables# could help to generate markdown table
note: for newline in markdown table, you have to input
特别例子 - 线程 thread.sleep, or readBytes from console,但是仍然CPU很高
E.g. High CPU while read lines from console: http://www.jianshu.com/p/4a6fe6c82311
E.g. 微信群中,有人提出在一台测试Tomcat(没有任何load),但是cpu仍使用了9%左右。通过jstack发现cpu最高的线程stack为Thread.sleep
此类问题相对来说更复杂一些,因为我们已经找到了相关的繁忙代码,但是这类代码不应该造成cpu繁忙。如read from console应该永远都不到,因为没有人输入; 如 thread.sleep不应该造成high cpu。
这里我给出一个思路
使用jvisualvm继续看一下,能否找到繁忙method的具体情况。可能在这一步就找到了问题根本原因
查看相关源代码
如:read lines from console这个例子,通过查看源代码可知,我们不期望从console读到任何东西,那么为什么读到了?读到了什么值? 可以通过重新编译源代码或者btrace注入的方式,看一下到底读到的什么值,进一步查看。那篇文章中,作者直接在console read之后,加入了一个sleep,简单粗暴的解决了这个问题。其实也没啥问题。
如:tomcat sleep的例子,通过检查源代码,
at java.lang.Thread.sleep(NativeMethod)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java: 1355)
at java.lang.Thread.run(Thread.java:748)
如果真正的在sleep,cpu肯定不会高。我怀疑下面的backgroundProcessorDelay值有问题。
while (!threadDone) {
try {
Thread.sleep(backgroundProcessorDelay * 1000L);
} catch (InterruptedException e) {
// Ignore
}
if (!threadDone) {
processChildren(ContainerBase.this);
}
}
这个可以通过btrace注入Thread.sleep方法,查看它到底调用的值是多少。
例子:查看哪个线程最繁忙(下面脚本可以直接复制粘贴到你的linux shell中)
直接复制下面的几行,就可以得到高cpu的那些线程,以及一个完整的jstack输出文件
process_id=YOUR_PROCESS_ID_TO_BE_REPLACED
tmp_jstack_file=/tmp/$(date '+%Y%m%d_%H%M%S')_$USER_$$_${process_id}_jstack_$RANDOM
jstack ${process_id} > $tmp_jstack_file
ps -Leo pid,lwp,user,comm,pcpu --no-headers| grep "^[[:space:]]\+${process_id}[[:space:]]\+" | sort -k5 -r -n | head -n"${top_count:-5}" | while read thread_line
do
threadI