Linux定位最耗CPU的线程

定位Linux耗CPU较高的线程

压测过程,经常遇见服务器CPU非常高,只是单纯的top,也只能看到是哪个进程比较耗CPU,不能进一步定位具体的线程。

Linux定位CPU瓶颈几种方式

系统查看

查看物理CPU个数:
1
cat /proc/cpuinfo |grep "physical id"|sort|uniq|wc -l
查看每个物理CPU中的core个数:
1
cat /proc/cpuinfo |grep "cpu cores"|wc -l
逻辑CPU个数
1
cat /proc/cpuinfo |grep "procetestr"|wc -l

物理cpu个数*核数=逻辑cpu个数(不支持超线程技术的情况下)

uptime

1
2
[root@izuf6a1s9ws9726xatwn47z tools]# uptime
10:15:33 up 54 days, 18:01, 1 user, load average: 1.02, 0.56, 0.30
  • load average三值大小一般不能大于系统CPU的个数。
    1
    系统有8个CPU,如load average三值长期大于8,说明CPU很繁忙,负载很高,可能会影响系统性能。
  • 但偶尔大于8,一般不会影响系统性能。
  • 如load average输出值小于CPU个数,则表示CPU有空闲时间片,比如本例中的输出,CPU是非常空闲的

vmstat

1
2
3
4
5
6
[root@izuf6b0a7e5agj4lm7aj3mz ~]# vmstat 2 3
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
3 0 0 12267888 166640 1863724 0 0 1 41 72 88 3 1 96 0 0
2 0 0 12267872 166640 1863724 0 0 0 0 66000 163918 15 17 68 0 0
2 0 0 12267872 166640 1863724 0 0 0 158 18689 43630 40 5 55 0 0

r–运行和等待cpu时间片的进程数,这个值如果长期大于系统CPU的个数,说明CPU不足,需要增加CPU
b–在等待资源的进程数,比如正在等待I/O、或者内存交换等。
CPU
us
用户进程消耗的CPU时间百分比
us的值比较高时,说明用户进程消耗的cpu时间多,但是如果长期大于50%,就需要考虑优化程序或算法。

sy
内核进程消耗的CPU时间百分比,Sy的值较高时,说明内核消耗的CPU资源很多
根据经验,us+sy的参考值为80%,如果大于80%可能存在CPU资源不足。

sar

sar对系统每个方面进行单独统计,但会增加系统开销,不过开销可以评估,对系统的统计结果不会有很大影响

1
2
3
4
5
6
7
8
9
10
[root@izuf6b0a7e5agj4lm7aj3mz ~]# sar -u 3 5
Linux 3.10.0-693.2.2.el7.x86_64 (izuf6b0a7e5agj4lm7aj3mz) 08/29/2018 _x86_64_ (4 CPU)

03:05:20 PM CPU %user %nice %system %iowait %steal %idle
03:05:23 PM all 0.08 0.00 0.08 0.08 0.00 99.75
03:05:26 PM all 0.08 0.00 0.00 0.00 0.00 99.92
03:05:29 PM all 0.08 0.00 0.08 0.00 0.00 99.83
03:05:32 PM all 0.33 0.00 0.08 0.00 0.00 99.58
03:05:35 PM all 0.08 0.00 0.08 0.00 0.00 99.83
Average: all 0.13 0.00 0.07 0.02 0.00 99.78

解释:

1
2
3
4
5
6
%user列显示了用户进程消耗的CPU 时间百分比。
%nice列显示了运行正常进程所消耗的CPU 时间百分比。
%system列显示了系统进程消耗的CPU时间百分比。
%iowait列显示了IO等待所占用的CPU时间百分比
%steal列显示了在内存相对紧张的环境下pagein强制对不同的页面进行的steal操作 。
%idle列显示了CPU处在空闲状态的时间百分比。

top

1
2
3
4
5
6
7
top - 10:20:34 up 54 days, 18:06,  1 user,  load average: 0.14, 0.30, 0.26
Tasks: 101 total, 1 running, 100 sleeping, 0 stopped, 0 zombie
%Cpu(s): 1.3 us, 0.5 sy, 0.0 ni, 98.2 id, 0.1 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 16267724 total, 3338792 free, 6439248 used, 6489684 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 9492508 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND

第一行为当前时间,系统运行时间,当前登录用户数,系统1/5/15分钟平均负载

第二、三行为进程和CPU的信息。当有多个CPU时,这些内容可能会超过两行。内容如下:
Tasks:

total 进程总数
running 正在运行的进程数
sleeping 睡眠的进程数
stopped 停止的进程数
zombie 僵尸进程数

Cpu(s):

us 用户空间占用CPU百分比
sy 内核空间占用CPU百分比
ni 用户进程空间内改变过优先级的进程占用CPU百分比
id 空闲CPU百分比
wa 等待输入输出的CPU时间百分比
hi 硬中断(Hardware IRQ)占用CPU的百分比
si 软中断(Software Interrupts)占用CPU的百分比

st (Steal time) 是当 hypervisor 服务另一个虚拟处理器的时候,虚拟 CPU 等待实际 CPU 的时间的百分比。

最后两行为内存信息。内容如下:
Mem:

total 物理内存总量
used 使用的物理内存总量
free 空闲内存总量
buffers 用作内核缓存的内存量

Swap:

total 交换区总量
used 使用的交换区总量
free 空闲交换区总量
cached 缓冲的交换区总量。
内存中的内容被换出到交换区,而后又被换入到内存,但使用过的交换区尚未被覆盖,
该数值即为这些内容已存在于内存中的交换区的大小。
相应的内存再次被换出时可不必再对交换区写入。

进程信息区
统计信息区域的下方显示了各个进程的详细信息。首先来认识一下各列的含义。
序号 列名 含义
PID 进程id
PPID 父进程id
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 键可以将当前的排序倒转。

定位具体线程

top -c

通过top -c获得当前系统资源总览,键盘输入P(大写),根据对CPU消耗倒序排列,获得最耗CPU的进程PID

1
2
3
4
5
6
7
8
9
10
11
Tasks: 122 total,   1 running, 121 sleeping,   0 stopped,   0 zombie
%Cpu(s): 90.3 us, 1.6 sy, 0.0 ni, 6.6 id, 0.0 wa, 0.0 hi, 1.5 si, 0.0 st
KiB Mem : 16267200 total, 7578856 free, 6915592 used, 1772752 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 9038816 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4209 testadmin 20 0 15.470g 5.913g 23928 S 745.5 38.1 209:33.97 /home/testadmin/jdk1.8.0_191/bin/java -D[Server:server-one] -D[pcid:499421770] -Xms7168m -Xmx13312m -server -XX:Metas+
9 root 20 0 0 0 0 S 0.3 0.0 0:08.16 [rcu_sched]
18 root 20 0 0 0 0 S 0.3 0.0 0:00.06 [ksoftirqd/2]
1264 root 20 0 2517268 69256 10948 S 0.3 0.4 4:53.38 /usr/local/cloudmonitor/jre/bin/java -Djava.compiler=none -XX:-UseGCOverheadLimit -XX:NewRatio=1 -XX:SurvivorRatio=8+
1 root 20 0 43660 4076 2492 S 0.0 0.0 0:01.79 /usr/lib/systemd/systemd --switched-root --system --deserialize 21

以上例子,最耗CPU的进程PID为4209

top -Hp PID

top -Hp PID获得该PID的线程列表,通过大写P,倒序排列,获取最耗CPU的线程PID:

1
2
3
4
5
6
7
8
%Cpu(s): 91.3 us,  1.9 sy,  0.0 ni,  5.5 id,  0.0 wa,  0.0 hi,  1.4 si,  0.0 st
KiB Mem : 16267200 total, 7266940 free, 7226952 used, 1773308 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 8727236 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
10185 testadmin 20 0 15.470g 6.208g 23928 S 8.2 40.0 0:12.15 java
10148 testadmin 20 0 15.470g 6.208g 23928 S 6.8 40.0 0:12.21 java
10149 testadmin 20 0 15.470g 6.208g 23928 R 6.8 40.0 0:12.18 java

例如上述例子,获得最耗CPU的线程PID为10185

printf “%x”

通过printf “%x” 线程PID,将PID转换为16进制:

1
2
[testadmin@izuf6bp7m03kbyap05zr6gz themes]$ printf “%x” 10185
“27c9”

获得16进制的PID27c9

jstack 进程PID | grep 16进制线程PID -C5 –color

通过jstack 进程PID | grep 16进制线程PID -C5 --color,获得耗CPU的线程详情:

1
2
3
4
5
6
7
8
9
10
11
12
[testadmin@izuf6bp7m03kbyap05zr6gz themes]$ jstack 4209 | grep 27c9 -C5 --color
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1985)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1487)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1364)
at java.lang.Thread.run(Thread.java:748)

"default task-417" #605 prio=5 os_prio=0 tid=0x00000000053cf800 nid=0x27c9 waiting on condition [0x00007f19f68f6000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000048002c208> (a java.util.concurrent.Semaphore$FairSync)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos(AbstractQueuedSynchronizer.java:1037)

上述例子,可以获取最耗CPU的线程名称:default task-417,以及该线程执行的代码堆栈:0x27c9

以上,完!

文章目录
  1. Linux定位CPU瓶颈几种方式
    1. 系统查看
      1. 查看物理CPU个数:
      2. 查看每个物理CPU中的core个数:
      3. 逻辑CPU个数
    2. uptime
    3. vmstat
    4. sar
    5. top
  2. 定位具体线程
    1. top -c
    2. top -Hp PID
    3. printf “%x”
    4. jstack 进程PID | grep 16进制线程PID -C5 –color
|