目录
1、什么是cpu高的问题?
在实际的生产环境中,对程序的性能、占用的资源其实是很敏感的。在这个大环境下,如果某个进程占用的cpu异常的高,势必需要分析cpu高的原因、以及减少其cpu资源
2、为什么会发生cpu高的问题?
如果需要分析cpu高的原因,就需要对cpu高的原因,有一些初步的判断。一般可以概括为以下几种情况:
- 代码中写死循环时,一直占用cpu
- 循环中不停的创建对象,也会导致GC频繁
- System.currentTimeMillis() 采用这种方式去做计时,大概占用了10-20%cpu,因为不停的调用
- FullGC 问题
C2 CompilerThread0这个线程占用了比较高的CPU。C2 Compiler 是JVM在server模式下字节码编译器,JVM启动的时候所有代码都处于解释执行模式,当某些代码被执行到一定阈值次数,这些代码(称为热点代码)就会被 C2 Compiler编译成机器码,编译成机器码后执行效率会得到大幅提升。
3、怎么分析cpu高的问题?
首先,针对CPU高的问题,先要知道哪个进程中的哪个线程的cpu比较高。使用命令top -H -p pid,可以看到进程号为pid的进程中,哪些线程的cpu比较高:29871
top - 15:26:12 up 218 days, 17:55, 4 users, load average: 1.17, 1.16, 1.13
Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 5.7 us, 0.4 sy, 0.0 ni, 93.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 13181523+total, 582976 free, 11175036+used, 19481896 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 18987672 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
29871 ossuser 20 0 2236272 547756 18820 R 30.90 1.665 30:38.18 pool-12-thread-
29876 ossuser 20 0 2236272 547756 18820 S 3.987 1.665 3:25.98 pool-14-thread-
29872 ossuser 20 0 2236272 547756 18820 S 2.990 1.665 2:46.98 pool-13-thread-
22162 ossuser 20 0 2236272 547756 18820 S 1.329 1.665 0:56.85 C2 CompilerThre
22128 ossuser 20 0 2236272 547756 18820 S 0.997 1.665 0:51.67 java
22131 ossuser 20 0 2236272 547756 18820 S 0.997 1.665 0:51.30 java
22165 ossuser 20 0 2236272 547756 18820 S 0.997 1.665 0:54.94 C2 CompilerThre
接着,需要知道占用cpu比较高的这个线程在做什么事情。
如果很幸运的,你可以根据线程的COMMAND知道他是什么线程,那就太好了,你可以排查这个线程是否有死循环,或者由很大的集合需要遍历。
如果并不能通过线程的COMMAND知道他是什么线程,或者就算知道他是哪个进程但是分析不出来到底什么原因造成这个线程占用的cpu很高。那么你就需要抓到线程栈。
使用命令:jstack pid > jstack.log
"DestroyJavaVM" prio=5 tid=7f9712001000 nid=0x110247000 waiting on condition [00000000]
java.lang.Thread.State: RUNNABLE
"Thread-21" prio=5 tid=7f9712944000 nid=0x118d76000 waiting for monitor entry [118d75000]
java.lang.Thread.State: BLOCKED (on object monitor)
at threads.deadlock.DeadlockDemo$BadTransferOperation.transfer(DeadlockDemo.java:86)
- waiting to lock <7f3366f58> (a threads.deadlock.Account)
- locked <7f3366ee0> (a threads.deadlock.Account)
at threads.deadlock.DeadlockDemo$BadTransferOperation.run(DeadlockDemo.java:59)
"Thread-20" prio=5 tid=7f971216c000 nid=0x118c73000 waiting for monitor entry [118c72000]
java.lang.Thread.State: BLOCKED (on object monitor)
at threads.deadlock.DeadlockDemo$BadTransferOperation.transfer(DeadlockDemo.java:86)
- waiting to lock <7f3366e98> (a threads.deadlock.Account)
- locked <7f3366f58> (a threads.deadlock.Account)
at threads.deadlock.DeadlockDemo$BadTransferOperation.run(DeadlockDemo.java:59)
"Thread-19" prio=5 tid=7f9712943800 nid=0x118b70000 waiting for monitor entry [118b6f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at threads.deadlock.DeadlockDemo$BadTransferOperation.transfer(DeadlockDemo.java:81)
- waiting to lock <7f3366f40> (a threads.deadlock.Account)
at threads.deadlock.DeadlockDemo$BadTransferOperation.run(DeadlockDemo.java:59)
"Thread-18" prio=5 tid=7f9712942800 nid=0x118a6d000 waiting for monitor entry [118a6c000]
java.lang.Thread.State: BLOCKED (on object monitor)
at threads.deadlock.DeadlockDemo$BadTransferOperation.transfer(DeadlockDemo.java:81)
- waiting to lock <7f3366f40> (a threads.deadlock.Account)
at threads.deadlock.DeadlockDemo$BadTransferOperation.run(DeadlockDemo.java:59)
"Thread-17" prio=5 tid=7f9712942000 nid=0x11896a000 waiting for monitor entry [118969000]
java.lang.Thread.State: BLOCKED (on object monitor)
at threads.deadlock.DeadlockDemo$BadTransferOperation.transfer(DeadlockDemo.java:81)
- waiting to lock <7f3366ec8> (a threads.deadlock.Account)
at threads.deadlock.DeadlockDemo$BadTransferOperation.run(DeadlockDemo.java:59)
得到线程栈之后,需要怎么分析呢?
1、首先,需要知道线程栈的基本概念:
线程状态有:
死锁, Deadlock(重点关注)
执行中,Runnable
等待资源, Waiting on condition(重点关注)
等待获取监视器, Waiting on monitor entry(重点关注)
暂停,Suspended
对象等待中,Object.wait() 或 TIMED_WAITING
阻塞, Blocked(重点关注)
停止,Parked
2、线程栈要怎么看?
Thread-20 | The thread’s name as described above. |
prio=5 | The thread's priority. A number from 1 to 10, where 1 is the lowest and 10 is the highest priority. |
tid=7f971216c000 | The thread id. A unique number that’s returned by a Thread.getId() call. |
nid=0x74af | The native thread id. This maps to a platform dependent thread id. |
waiting for monitor entry [118c72000] java.lang.Thread.State: BLOCKED (on object monitor) | This is the status of the thread; in this case it’s BLOCKED. Also included is a stack trace outlining where the thread is blocked. |
其中把nid等都是16进制,把29871转成16进制,即可看到0x74af的nid
3、观察是否发生线程死锁或者死循环。
Tips:top -H -p pid命令详解:
第一行:任务队列信息
跟 uptime 命令的执行结果一样,说明如下:
15:26:12 系统当前时间
up 218 days, 17:55 系统的运行时间:218天17个小时55分钟
4 users 当前登录用户数
load average: 1.17, 1.16, 1.13 系统负载,即任务队列的平均长度,三个数据分别是1分钟、5分钟、15分钟前到现在的平均值
第二行:进程信息
说明如下:
Tasks: 1 total , 0 running, 1 sleeping, 0 stopped, 0 zombie
进程总数,正在运行的进程数,休眠的进程数,停止的进程数,僵尸进程数
第三行:CPU信息
说明如下:
%Cpu(s): 5.7 us, 0.4 sy, 0.0 ni, 93.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
用户空间占用CPU百分比
内核空间占用CPU百分比
用户进程空间内改变过优先级的进程占用CPU百分比
空闲CPU百分比
等待输入输出的CPU时间百分比
硬件CPU中断占用百分比
软中断占用百分比
虚拟机占用百分比
第四行:内存信息
说明如下:
KiB Mem : 13181523+total, 582976 free, 11175036+used, 19481896 buff/cache
13181523 total 物理内存总量
582976 used 使用的物理内存总量
11175036 free 空闲内存总量
19481896 buffers 用作内核缓存的内存量
第五行:交换区信息
说明如下:
KiB Swap: 0 total, 0 free, 0 used. 18987672 avail Mem
total 交换区总量
used 使用的交换区总量
free 空闲交换区总量
cached 缓冲的交换区总量(内存中的内容被换出到交换区,而后又被换入到内存,但使用过的交换区尚未被覆盖,该数值即为这些内容已存在于内存中的交换区的大小,相应的内存再次被换出时可不必再对交换区写入)
第五行:应用进程信息
说明如下:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
24412 tomcat 20 0 13.342g 2.870g 13808 S 3.0 2.3 190:18.39 java
详细说明:
序号 列名 含义
a PID 进程id
b PPID 父进程id
c RUSER Real user name
d UID 进程所有者的用户id
e USER 进程所有者的用户名
f GROUP 进程所有者的组名
g TTY 启动进程的终端名。不是从终端启动的进程则显示为 ?
h PR 优先级
i NI nice值。负值表示高优先级,正值表示低优先级
j P 最后使用的CPU,仅在多CPU环境下有意义
k %CPU 上次更新到现在的CPU时间占用百分比
l TIME 进程使用的CPU时间总计,单位秒
m TIME+ 进程使用的CPU时间总计,单位1/100秒
n %MEM 进程使用的物理内存百分比
o VIRT 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES
p SWAP 进程使用的虚拟内存中,被换出的大小,单位kb。
q RES 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA
r CODE 可执行代码占用的物理内存大小,单位kb
s DATA 可执行代码以外的部分(数据段+栈)占用的物理内存大小,单位kb
t SHR 共享内存大小,单位kb
u nFLT 页面错误次数
v nDRT 最后一次写入到现在,被修改过的页面数。
w S 进程状态(D=不可中断的睡眠状态,R=运行,S=睡眠,T=跟踪/停止,Z=僵尸进程)
x COMMAND 命令名/命令行
y WCHAN 若该进程在睡眠,则显示睡眠中的系统函数名
z Flags 任务标志,参考 sched.h
默认情况下仅显示比较重要的 PID、USER、PR、NI、VIRT、RES、SHR、S、%CPU、%MEM、TIME+、COMMAND 列。可以通过下面的快捷键来更改显示内容。
更改显示内容通过 f 键可以选择显示的内容。按 f 键之后会显示列的列表,按 a-z 即可显示或隐藏对应的列,最后按回车键确定。
按 o 键可以改变列的显示顺序。按小写的 a-z 可以将相应的列向右移动,而大写的 A-Z 可以将相应的列向左移动。最后按回车键确定。
按大写的 F 或 O 键,然后按 a-z 可以将进程按照相应的列进行排序。而大写的 R 键可以将当前的排序倒转。
更多内容请关注微信公众号
官方公众号 | 外里科技 |
运营公众号 | 英雄赚 |
微信 | wxid_8awklmbh1fzm22 |
1247408032 | |
开源代码 | https://gitee.com/B_T/beimi |