6. CPU消耗分析
在 Linux 中, CPU 主要用于中断、内核及用户进程的任务处理,优先级为中断 > 内核 > 用户进程,在学习如何分析 CPU 消耗状况前,还有三个重要的概念要阐述。
##概念
上下文切换
每个 CPU (或多核心 CPU 中的每核 CPU )同一时间只能执行一个线程, Linux 采用的是抢占式调度,即为每个线程分配一定的执行时间。线程执行时间到达而切换到其它线程时,就会触发上下文切换。上下文切换过多会造成内核占据较多的 CPU 使用,使得应用的响应速度下降。
对于JAVA应用,典型的是进行文件IO操作、网络IO操作、锁等待或线程sleep时,当线程进入阻塞或等待,会触发上下文切换
运行队列
每个 CPU 都维护了一个可运行的线程队列。
例如:4核CPU,JAVA应用启动了8个线程,且都是出于运行状态,那么在平均分配情况下,每个CPU中的运行队列就有2个线程
通常系统的load由CPU运行队列来决定,运行队列值越大则线程需要消耗越长的时间才能执行完
通常建议控制在每个 CPU 核上的运行队列为 1-3 个。
利用率
CPU 利用率为 CPU 在用户进程、内核、中断处理、IO等待以及空亲五个部分使用的百分比。
建议用户进程和内核进程消耗的 CPU 比率在 65%-70%/30%-35% 左右。
分析方式
在linux中,通过top或pidstat来查看进程中线程的CPU消耗情况
top
top命令可以查看CPU消耗情况
参数 | 意义 |
---|---|
us | 表示用户进程处理所占的百分比 |
sy | 表示内核进程所占的百分比 |
ni | 表示被 nice 命令改变优先级的任务所占的百分比 |
id | 表示 CPU 空闲时间所占的百分比 |
wa | 表示为在执行的过程中等待 IO 所占的百分比 |
hi | 表示为硬件中断所占的百分比 |
si | 表示为软件中断所占的百分比 |
对于多核CPU,上图显示的为多个CPU占用百分比总和,如果需要查看每个核的消耗情况
进入top视图后,按 1
默认情况下,top视图显示为进程CPU的消耗情况。
top 视图按 shift+h 后,可按线程查看 CPU 的消耗状况
pidstat
pidstat 是 SYSSTAT 中的工具,如需使用,请先安装
pidstat 1 2 ,在 console 会每隔 1 秒输出目前活动进程的 CPU 消耗状况,共 2 次。
其中CPU表示当前进程使用到的CPU个数
如需查看某进程中线程的CPU消耗:
pidstat –p [PID] –t 1 5 可查看某进程中线程的 CPU 消耗状况
图中TID为线程ID,pidstat好处:可查看每个线程的具体CPU使用率的情况
除了top和pidstat外,还可以采用vmstat来采样,查看CPU的上下文切换、运行队列和使用率情况
CPU消耗严重时的表现
当 CPU 消耗严重时,主要体现在 us 、 sy 、 wa 或 hi 的值变高。对 JAVA 应用而言,主要体现在 us 、 sy 两个值上:
us
us 值过高,表示运行的应用消耗了大部分的 CPU ,如下是找出具体消耗 CPU 的线程的办法:
- 首先通过 top 或 pidstat 命令找出消耗 CPU 严重严重的线程及其 ID ,将些 ID 转化为十六进制的值。
- 之后通过 kill -3 [javapid]或jstack的方式dump出应用的java线程信息,通过之前转化的十六进制的值找到对应的nid值的线程。该线程即为消耗 CPU 的线程。
- 可多执行几次上述过程,以确保找到真实的消耗 CPU 的线程。
原因
- 线程一直处于可运行状态(Runnable),通常是线程在执行无阻塞、循环、正则或纯粹的计算等
- 频繁的GC
- 如每次请求分配较多内存,访问量高时,出现频繁GC。 进而堆积更多的请求,消耗的内存严重。 最严重时,导致full gc
频繁GC的情况要通过JVM内存的消耗来查找原因
sy
sy 值高时,表示系统花费了更多的时间在进行线程切换,JAVA应用造成这种现象的主要原因是启动的线程比较多,且这些线程多数都处于不断的阻塞和执行状态的变化过程中,导致系统不断的切换线程,产生大量的上下文切换。
此时,对JAVA应用而言,主要是找到线程不断切换的原因。可以采用
kill -3 [javapid]或jstack -l [javapid]的方式dump出程序的线程信息,找出等待状态或锁过多的线程
1 | public class SyHighDemo { |
由上可知:CPU在cs和sy上消耗很大,运行时使用jstack -l 查看程序状况,可知启动了很多线程,并且很多线程都处于TIMED_WAITING(on object monitor)状态和runnable状态的转换中。
通过on object monitor对应的堆栈信息,可知:系统中锁竞争激烈,这就是导致线程上下文切换频繁的原因
总结
- 如果us过高,表明应用程序占用了过多cpu
- 可以用top命令查看到是哪个进程的哪些线程耗费了过多cpu,然后用jstack -l pid导出dump,然后查找对应pid的十六进制,看看是哪个线程调了什么方法。
- 解决方法:增加线程sleep,优化算法。
- 如果sy过高,表明大量的用户线程运行堵塞导致系统 频繁进行上下文切换。
- 可用jstack -l 查看线程运行状况,并配合vmstat 查看系统执行情况及cpu情况。
- 解决方法:减少线程数,引入协程