01 前言
版权声明:本文为CSDN博主「Μr.ηobοdy」的原创文章
原文链接:
cpu是时分(time division)的,操作系统里有很多线程,每个线程的运行时间由cpu决定,cpu会分给每个线程一个时间片,时间片是一个很短的时间长度,如果在时间片内,线程一直占有,则是100%;我们应该意识到,cpu运行速度很快(主频非常高),除非密集型耗费cpu的运算,其它类型任务都会在小于时间片的时间内结束。
java cpu100%的排查步骤,基本都是一模一样的,只是命令稍有区别!步骤如下:
- 查找消耗cpu最高的进程PID
- 根据PID查出消耗cpu最高的线程号
- 根据线程号查出对应的java线程,进行处理。
02 Demo模拟
构造一个请求接口,模拟无限产生Person实例。
并进行接口调用:
@RestController@RequestMapping("demo")publicclassDemoController{@GetMapping("test")publicbooleantest(@RequestParamboolean justDo){
List<Person> persons =newArrayList<>();int i =1;if(justDo){while(true){
persons.add(newPerson("张三", i));
System.out.println(persons.size());}}return justDo;}}03 排查过程
1.使用
top
命令找出cpu占用最高的进程
jstack 29869 > 29869.tdump
cat 29869.tdump
5.将前一步骤查出的3个线程PID从十进制转为十六进制,因为java线程栈文件中的线程id是十六进制。对应分别为
29871 -> 0x74af,,29872 -> 0x74b0,29873 -> 0x74b1
。发现此3个线程中有2个为gc线程和1个工作线程。gc线程忙碌表示内存不够用了,要进行内存回收,可能是java内存回收不了,导致一直gc。
| 名字 | 解析 |
|---|---|
| S0 | 年轻代中第一个survivor(幸存区)已使用的占当前容量百分比 |
| S1 | 年轻代中第二个survivor(幸存区)已使用的占当前容量百分比 |
| E | 年轻代中Eden(伊甸园)已使用的占当前容量百分比 |
| O | old代已使用的占当前容量百分比 |
| M | 元数据区使用比例 |
| CCS | 压缩使用比例 |
| YGC | 从应用程序启动到采样时年轻代中gc次数 |
| YGCT | 从应用程序启动到采样时年轻代中gc所用时间(s) |
| FGC | 从应用程序启动到采样时old代(全gc)gc次数 |
| FGCT | 从应用程序启动到采样时old代(全gc)gc所用时间(s) |
| GCT | 从应用程序启动到采样时gc用的总时间(s) |
7.使用
jmap -dump:live,format=b,file=pid.hprof pid
命令导出堆文件,只导出live的对象。文件后缀名可以是任意的,因为它也是二进制的,不过通常以hprof结尾。
8.使用
JAVA_HOME/bin/jvisualvm.exe
工具分析快照。载入快照(文件----->载入—>文件类型(堆)):
选择类列表,按照大小排序,找出占用内存最大的类别,发现是Person类。
至此,问题找到原因,原来是在死循环中,不断生产Person实例,并且无法回收,不仅工作线程一直占用cpu,而且导致gc线程忙碌进行回收内存,但是回收不了,最后导致内存不足
java.lang.OutOfMemoryError
。
java的bin目录下有很多JVM性能调优监控工具jps、jstack、jmap、jhat、jstat、hprof。


发布评论