一般CPU的性能都是比较过剩的,随便打开一台电脑,他的内存可能会被撑爆,但是cpu撑爆就相对比较少见了,因为CPU够快啊
就拿i7 8750H举例,6核心12线程
默认频率2.2GHz, 单核turbo 4.1GHz,四核睿频4.0GHz,全核满载3.9GHz
这个数值很不错了,即使以最低的计算,2.2GHz意味每秒可以计算20亿次左右,这个数据太多了,所以cpu才会分时计算
在线上的环境中,一般cpu飙高极大的可能性是出现了死循环了.
今天我们就来模拟一次这样的情况,以及出现这样的情况了,我们又该如何来进行定位找到代码中出现死循环的地方
1.统一环境 springboot 2.1.3.RELEASE,开启web支持
2.新建一个控制层,添加如下代码
@GetMapping("/letCpuBusy")
public void letCpuBusy(){
int i=0;
while(true) {
i++;
}
}
代码是非常的简单,就是不停的加数字.循环永远不会退出来,所以会一直让cpu在运转
3.ok,打包,上传到阿里云的机器,运行
访问3次上面的这个接口
curl 127.0.0.1:8080/letCpuBusy
ok了,
可是问题来了,我们运行了死循环的程序,可是我们表面上是看不出来的啊?
我们访问这个应用的其他的接口照样是可以访问的.
这个时候一个linux 的命令就派上用户了
top
top命令可以查看linux 主机的各种健康状态,当然也包含cpu 的
我们先执行一次top 看一下
注意看红框处,效果上来了 是不是?
我们跑了3个死循环,让这个java 的应用cpu利用率 飙到了99.7%
这个应用其实是不正常的了.正常情况下是不会这么高的.
ok 回到问题,由于这个是我们手动构建的,我们知道是什么原因导致的,在真实的环境中,怎么知道是哪里的代码出现了死循环么?
这个时候一个工具就登场了:jstack
4.jstack可以导出java进程的所有线程信息
先查看java应用的pid信息
jps -l
然后
jstack pid >123.txt
把这个文件下载到自己的电脑上,这个文件保存了java 应用的所有的线程信息
5.我们知道进程下面可以分为线程,刚才我们访问了3次死循环,所以,这个java 应用里面应该与3个死循环的线程
那么怎么办查看了?
top -p pid -H
正好是3个, 所以所以7122 7120 7121 这3个pid 就是对应的那3个死循环的线程id了
只不过这3个数字都是10进制的,需要转一下16进制
7122D-> 1bd2H
7120D-> 1bd0H
7121D-> 1bd1H
以第一个具体,打开刚才的那个123.txt文件
搜索1bd2
jstack中已经把完整的调用堆栈都打印出来了
观察线程状态是RUNNABLE,正在运行中
第一行给了我们提示代码的位置
at com.example.demo.controller.UserController.letCpuBusy(UserController.java:61)
下面看下代码
正好就是61行
至此问题都已经定位到了
总结一下,一般按情况下cpu飙高还是比较少见的,因为cpu一直都是很强的,但是如果有一天在真实的环境中遇到这样的情况了,还是要有一定的思路去解决这样的问题,而不是惊慌失措不知道干怎么办.
相较于分析内存溢出的问题,cpu 的飙高分析要简单的多.也好定位和分析.
写这篇文章的时候恰好看到甲方的人在公司群讨论cpu飙高的问题
这个是mongo的程序的cpu飙高了,mongo作为一款广泛使用的数据库,他的cpu占用这么高,应该不太可能是mongo本身的原因,
就好比你用mysql数据库不停的执行select * from xxx 大表查询一样,问题还是出在开发本身,所以在开发过程中要注重下sql 或者 nosql 的查询执行的性能,当然这个是相当考验基本功的,必须要有长足的开发经验和实践.
学习之路还漫长,加油!
补充一下吧.上次在一次面试的过程中,2个面试官,其中一个人问我 jstack 怎么用,当然顺口就说出来了.
问了一会儿,另外一个人问我有没有遇到过死锁,遇到死锁该怎么解决?
我说没有,在平时哪会遇到死锁什么的.
这个时候面试官说,其实就是你刚才说的那个jstack可以定位
那个时候突然一想,对啊,死锁不就是cpu在等待么.jstack 不就是查看线程栈么.
所以要活学活用啊,仅仅是记住一些命令的功能,没有扩展,惭愧啊.