背景
应用出问题排除网络和低级的程序bug.
最多的是两种情况:
CPU爆掉了和内存爆掉了.
CPU爆掉可以理解为是 指令周期的泄露/溢出
也就是未合理控制CPU的使用量.
内存爆掉可以理解为是 服务内存使用过多.
也就是未合理控制内存的用量
两者都可以理解为 信用卡刷爆了,并且没有能力还上
只能破产(宕机)
本次先讲解一下判断CPU问题的武器库.
基础工具
ps && top
Linux 系统里面最基础的进行CPU诊断的是 ps 和 top
两者其实不仅可以判断CPU的使用, 也可以判断内存的使用.
ps -eo <列名>
其中,<列名> 是你希望显示的列的名称,可以是以下任意组合:
pid:进程ID
ppid:父进程ID
user:进程所有者
%cpu:CPU使用率
%mem:内存使用率
vsz:虚拟内存大小
rss:常驻内存大小
tty:终端设备
stat:进程状态
start:进程启动时间
time:进程运行时间
command:启动命令
top 是另外一个很常见的命令.
ps查看信息
ps -eo pid,lstart,time,rss,cmd |grep 223148
223148 Fri Nov 15 18:01:15 2024 00:23:41 6856340 java
分别说明:
grep 和 显示的第一个都是进程pid
第一个是启动时间:
Fri Nov 15 18:01:15 2024
第三个是进程消耗的CPU周期时间.
00:23:41
第四个是进程使用的rss内存信息,但是是KB
6856340,约合不到7GB的空间.
最后一列是 java进程具体命令, 这里么有展示.
top简介
样例:
top - 15:44:21 up 2 days, 7:24, 3 users, load average: 0.01, 0.04, 0.07
Tasks: 331 total, 1 running, 330 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.4 sy, 0.0 ni, 99.6 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 47003.4 total, 17973.3 free, 23365.7 used, 5664.4 buff/cache
MiB Swap: 0.0 total, 0.0 free, 0.0 used. 23016.3 avail Mem
第一行:
15:44:21 当前系统时间
up 2 days, 7:24, 运行了 两天7小时24分钟
3 users, load average: 0.01, 0.04, 0.07 有3个用户登录, 最近系统的压力信息分别为 0.01等.
第二行:
正在运行的进程信息.
一共331个进程, 其中一个正在运行 330个在睡眠状态.
第三行:
CPU的使用信息
分别是用户态: 内核态: 通过改变优先级占用的比率: 空闲CPU: io等待时间: 硬中断时间: 软中断时间: 虚拟机被偷的运行时间.
需要说明, 一个好的应用程序, 肯定尽量使用用户态的CPU
如果内核态CPU比较高, 说明系统不停的进入内核态高权限进行运行,存在风险
数据库要重视 wa 指标, 太高了, 说明磁盘有问题. 他比io的util 更有用.
id 其实是另外应该很重要的事项, 100 - id 可以理解为cpu在干活的时间比率.
第四行:
内存的信息:
比如这个是 MB 的信息. 总计内存, free的内存, 使用的内存, 以及 buffer/cache的内存.
最后一行是包含了 swap 的内存信息, 其中 avail Mem 最关键.
可以理解为是 free + buffer/cache
top的使用方式之一
第一个 top -Hp $pid
样例: top -Hp 223148
结果为:
top - 16:01:15 up 2 days, 7:41, 3 users, load average: 0.00, 0.02, 0.01
Threads: 310 total, 0 running, 310 sleeping, 0 stopped, 0 zombie
%Cpu(s): 1.1 us, 0.4 sy, 0.0 ni, 98.4 id, 0.0 wa, 0.1 hi, 0.1 si, 0.0 st
MiB Mem : 47003.4 total, 18072.3 free, 23265.5 used, 5665.6 buff/cache
MiB Swap: 0.0 total, 0.0 free, 0.0 used. 23116.6 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
223149 root 20 0 19.0g 6.5g 107208 S 0.0 14.2 3:15.21 java
223209 root 20 0 19.0g 6.5g 107208 S 0.0 14.2 0:52.80 C2 CompilerThre
223210 root 20 0 19.0g 6.5g 107208 S 0.0 14.2 0:51.07 C2 CompilerThre
223204 root 20 0 19.0g 6.5g 107208 S 0.0 14.2 0:50.91 C2 CompilerThre
223205 root 20 0 19.0g 6.5g 107208 S 0.0 14.2 0:49.89 C2 CompilerThre
223203 root 20 0 19.0g 6.5g 107208 S 0.0 14.2 0:49.47 C2 CompilerThre
查看具体进程下面的线程信息:
跟top 一样, 其实有很多方式方法:
输入 大写M 按照内存排序, 输入大写P 按照当前使用的CPU 排序 输入 大写 T 按照线程使用的CPU时间排序.
注意这个是进行性能检测诊断的很重要和快捷的方法.
jdk 1.8. 202 + 以上的版本 可以显示 15个字节的线程信息. 可以区分线程的用途
与 jstack -l 里面的十六进制的线程号可以一一对应起来.
但是他不需要jstack的处理, 并且可以看到具体线程的执行消耗的CPU的时间. 非常简洁.
top的使用方式之二
第二个: top -bn 1
top 默认是显示不全的, 很多内容会被忽略掉
此时使用 top -bn 1 的方式可以将 top 的结果完整的打印出来,便于分析
跟 Hp 关联在一起, 可以看到所有进程的信息.
跟计划任务或者是 for 循环关联起来, 可以实现动态监控.
市面上的监控做不到 进程级别和线程级别的监控, 可以使用top的方式来实现.
监测间隔时间内的CPU使用差异, 线程CPU的时间增量如果有自然时间增长相近
那大概率是最近卡顿的一个原因, 如果有大量的fullGC大概率是因为这个线程导致的.
可以实现快速的大内存线程定位.
perf
yum install perf -y
然后执行命令为:
perf top -p 223148
部分结果为:
Samples: 110 of event 'cpu-clock', 4000 Hz, Event count (approx.): 22151010 lost: 0/0
Overhead Shared Object Symbol
28.27% [kernel] [k] finish_task_switch
6.40% libpthread-2.28.so [.] pthread_cond_timedwait
3.88% [kernel] [k] __lock_text_start
3.60% libjvm.so [.] ObjectMonitor::wait
2.84% [kernel] [k] _raw_spin_lock
2.57% libpthread-2.28.so [.] 0x000000000000e5e0
2.39% libjvm.so [.] Monitor::unlock
2.32% libjvm.so [.] os::sleep
2.31% libjvm.so [.] os::PlatformEvent::park
2.26% [kernel] [k] do_futex
一般可以根据 这个 信息进行判断, 是否存在严重的性能风险.
perf之二
perf stat -e task-clock -p $pid I 1000
类似结果为:
perf stat -e task-clock -p 223148 -I 1000
# time counts unit events
1.004622901 2.16 msec task-clock # 0.002 CPUs utilized
2.005543635 2.33 msec task-clock # 0.002 CPUs utilized
3.006027641 1.88 msec task-clock # 0.002 CPUs utilized
4.006799272 2.90 msec task-clock # 0.003 CPUs utilized
5.007645147 1.80 msec task-clock # 0.002 CPUs utilized
参数说明
-e 指定监控某些event
-I 指定报告间隔, 单位秒钟.
-p 指定进程号
需要说明 不指定 -e 的话 还能看到 io 和 cs 的信息
也比较关键.
然后可以使用excel将CPU使用情况展示出来.
pstack
pstack $pid
可以查看 进程的堆栈信息.
能够仔细查看部分内容.
pstack 223148 > 1.txt
cat 1.txt |grep '#0' |awk '{print $4}' |sort |uniq -c |sort -k1hr
154 pthread_cond_wait
98 epoll_wait
53 pthread_cond_timedwait
2 ??
2 recv
1 accept
sar
yum install sysstat -y
使用 sar 直接查看当天的CPU使用情况.
结果为默认十分钟的 平均值
可以通过
sar -f var/log/sa/sa15 查看15号的信息.
sar -f var/log/sa/sa15 -b 可以查看io信息.
-B 是page in 和out 的
-r 是内存相关
可以使用命令进行查看: sar --help
-B 分页状况 [A_PAGE]
-b I/O 和传输速率信息状况 [A_IO]
-d 块设备状况 [A_DISK]
-F [ MOUNT ]
文件系统统计信息 [A_FS]
-H 巨大页面利用率 [A_HUGE]
-I { <中断列表> | SUM | ALL }
中断信息状况 [A_IRQ]
-m { <关键字> [,...] | ALL }
电源管理统计信息 [A_PWR_...]
关键字:
CPU CPU 瞬时时钟频率
FAN 风扇速度
ebpf
ebpf 是一套很好的工具
通过动态修改内核 能够实现低侵入的性能监控.
主要工具有bcc 以及 直接python代码的写的工具
这一块需要后续完善.
Grafana_Prometheus
如果技术能力比较弱的话
可以部署node-exporter 加上grafana_prometheus
可以通过网页查看一些监控信息.
但是需要说明, 监控最多是趋势,可能较难将信息完全理解清晰.
numactl
可以通过numactl -H 查看不同numa节点的内存使用情况
命令: numactl -H
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 8
node 0 size: 515021 MB
node 0 free: 35960 MB
node 1 cpus: 24 25 26 27 28 29 30 31 32
node 1 size: 515028 MB
node 1 free: 36516 MB
node distances:
node 0 1
0: 10 20
1: 20 10
注意 机器的CPU 其实核心是 2路24核心,共计 96个线程
我这边删除了部分CPU的ID
如果不同numa节点的内存数量不一致的话. 可能会有一定的性能问题.
查看CPU的实际频率
find sys/devices/system/cpu -iname cpuinfo_cur_freq -exec cat {} \; \
|awk '{sum += $1} END {printf "NR = %d,Average = %3.3f\n",NR,sum/NR}'
NR = 96,Average = 2801000.000
可以看到运行的频率, 如果是物理机 并且压力比较小 他的频率会比较低一些.
proc的学习
cat > checkprocess.sh << EOF
let uptime=\`cat proc/sched_debug |grep ktime |awk '{print \$3}' |awk -F '.''{print \$1}'\`
let uptime=uptime/10
let pid=\`ps -ef |grep java |grep caf |head -n 1 |awk '{print \$2}'\`
for i in \`ls proc/\$pid/task \` ;
doecho -n \`cat proc/\$pid/task/\$i/stat |awk -F '(''{print \$2}' |awk -F ')''{print \$1}' \` ;
echo -n " 线程号: "
echo -n \`cat proc/\$pid/task/\$i/stat |awk '{print \$1}' \` ;
echo -n " 线程的启动后的时间(秒钟): "
let threadtime=\`cat proc/\$pid/task/\$i/stat | awk -F ')''{print \$2}' |awk '{print \$20}'\` ;
let threadtime=(uptime-threadtime)/100 ;
echo -n \$threadtime
echo -n " 线程占用的CPU时间(毫秒): "
let runingtime=\`cat proc/\$pid/task/\$i/sched |grep se.sum_exec_runtime |awk '{print \$3}' |awk -F '.''{print \$1}'\`
let runingtime=runingtime
echo \$runingtime
done
EOF
proc的说明
cat proc/sched_debug |grep ktime
查看开机之后的毫秒数.
cat proc/\$pid/task/\$i/stat |awk '{print \$1}
java进程内线程的相对于开机节点的毫秒数
let threadtime=\`cat /proc/\$pid/task/\$i/stat | awk -F ')' '{print \$2}' |awk '{print \$20}'\` ;
let threadtime=(uptime-threadtime)/100 ;
通过减法,计算出 java 特定线程的 运行市场, 也就是实际的存在时长.
let runingtime=\`cat /proc/\$pid/task/\$i/sched |grep se.sum_exec_runtime |awk '{print \$3}' |awk -F '.' '{print \$1}'\`
let runingtime=runingtime
echo \$runingtime
计算该进程的实际占用CPU的时间. 如果一个进程一直在占用CPU时间的话, 说明存在问题.
文章转载自济南小老虎,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




