文章目录
前言
工作几年了,最近面试CPU飙升至100%的问题,居然说不出个二五八万来,关键是二面和一面问题差不多,一面的人这么给机会,我一面后居然没有认真复盘。开诚布公的问我,你为什么二面和一面回答的差不多,没有任何提升呢。真是让人老脸一红,丢人丢到家了。
一、CPU为什么飙升?
- 无限循环或者高频计算
很常见的问题,代码错误,逻辑问题,高频计算任务。 - 资源分配不合理
现在的AI或者大数据运算,高峰期没有合理的分配资源。 - 锁竞争
死锁双方互相等待对方释放资源,无限循环,不拿对对方的资源不罢休。 - 大量的并发线程的问题
解决一个问题用了100个线程,频繁的进行CPU上下文的切换。 - 内存不足
会将磁盘存储作为虚拟内存使用,这种虚拟内存运行速度很慢。过度的分页和交换会导致CPU占用率居高不下 - 频繁的垃圾回收
垃圾回收会占用大量的CPU资源,对象频繁创建与销毁 - 内存泄露
可用内存减少,频繁的GC操作。
二、如何定位排查(java进程)
定位进程
使用top命令,可以看到信息分为两部分
lyl@localhost:~$ top
top - 18:26:30 up 50 min, 2 users, load average: 2.21, 1.07, 0.63
Tasks: 354 total, 2 running, 352 sleeping, 0 stopped, 0 zombie
%Cpu(s): 47.6 us, 11.8 sy, 0.0 ni, 39.7 id, 0.0 wa, 0.8 hi, 0.1 si, 0.0 st
MiB Mem : 15698.0 total, 10678.5 free, 3596.7 used, 1790.9 buff/cache
MiB Swap: 8032.0 total, 8032.0 free, 0.0 used. 12101.3 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
10187 lyl 20 0 506472 36964 30556 R 98.7 0.2 3:00.92 vmtoolsd
93082 lyl 20 0 7314356 87452 31900 S 98.3 0.5 1:10.66 java
22301 lyl 20 0 3158644 512336 117020 S 20.6 3.2 1:15.47 ptyxis
8676 lyl 20 0 5403608 428772 138056 S 12.0 2.7 1:03.01 gnome-shell
84554 root 20 0 0 0 0 I 2.0 0.0 0:00.61 kworker/u516:3-events_unbound
1726 root 20 0 3444640 97112 55940 S 0.3 0.6 0:16.05 dockerd
2635 root 20 0 1238496 17868 10536 S 0.3 0.1 0:10.43 containerd-shim
7553 lyl 20 0 6592 4288 2368 S 0.3 0.0 0:00.20 dbus-broker
8688 1001 20 0 10.7g 65392 46008 S 0.3 0.4 0:05.40 next-server (v1
93341 lyl 20 0 232248 5944 3768 S 0.3 0.0 0:00.11 top
94338 lyl 20 0 232248 6036 3860 R 0.3 0.0 0:00.02 top
### 第一部分:系统的总体情况,CPU、内存、交换空间的使用情况,进程数量和负载
us:1.3%的CPU时间被用户空间的进程(用户程序)占用
sy:1.2%的CPU时间被内核空间的进程占用
ni:0.0%的CPU时间被改变优先级的进程占用(优先级调整)
id:97.1%的CPU时间处于空闲状态
wa:0.0%的CPU时间等待I/O操作
hi:0.3%的CPU时间用于硬件中断
si:0.2%的CPU时间用于软件中断
st:0.0%的CPU时间被虚拟化环境占用(如果是虚拟机中运行的话)
后面的是总内存,空闲内存,已使用的内存,缓存和缓冲区的内存。
### 第二部分:总内存,空闲内存,已使用的内存,缓存和缓冲区的内存。
进程唯一标识,CPU和内存的使用情况,运行时间,进程的名称或者可执行文件名
- us和ni占用高,说明用户态进程占用了较多的CPU,所以应该重点排查进程的性能问题。
- 系统CPU高,说明内核态占用了较多的CPU,重点排查内核进程和系统调用的问题
- I/O等待CPU高,说明等待I/O的时间比较长,应该重点排查是不是出现了I/O的问题
- 软终端和硬中断高,说明中断的处理程序占用了较多的CPU,应该重点排序中断服务程序
定位进程中CPU占用率最高的线程
top -Hp pid 查看进程下面每个线程的CPU使用情况
lyl@localhost:~$ top -Hp 93082
top - 18:27:55 up 51 min, 2 users, load average: 2.28, 1.37, 0.78
Threads: 21 total, 1 running, 20 sleeping, 0 stopped, 0 zombie
%Cpu(s): 47.9 us, 12.1 sy, 0.0 ni, 39.2 id, 0.0 wa, 0.8 hi, 0.1 si, 0.0 st
MiB Mem : 15698.0 total, 10678.6 free, 3596.6 used, 1790.9 buff/cache
MiB Swap: 8032.0 total, 8032.0 free, 0.0 used. 12101.3 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
93083 lyl 20 0 7314356 87452 31900 R 98.7 0.5 2:34.71 java
93082 lyl 20 0 7314356 87452 31900 S 0.0 0.5 0:00.00 java
93084 lyl 20 0 7314356 87452 31900 S 0.0 0.5 0:00.00 GC Thread#0
93085 lyl 20 0 7314356 87452 31900 S 0.0 0.5 0:00.00 G1 Main Marker
93086 lyl 20 0 7314356 87452 31900 S 0.0 0.5 0:00.00 G1 Conc#0
93087 lyl 20 0 7314356 87452 31900 S 0.0 0.5 0:00.00 G1 Refine#0
93088 lyl 20 0 7314356 87452 31900 S 0.0 0.5 0:00.00 G1 Service
93091 lyl 20 0 7314356 87452 31900 S 0.0 0.5 0:00.05 VM Periodic Tas
线程PID十进制转换为16进制
top打印的十进制的,但是jstack打印的是十六进制的。
printf “%x\n” PID
lyl@localhost:~$ printf "%x\n" 93082
16b9a
使用jstack打印堆栈信息
lyl@localhost:~$ jstack 93082|greo 16b9a
Full thread dump Java HotSpot(TM) 64-Bit Server VM (17.0.12+8-LTS-286 mixed mode, sharing):
Threads class SMR info:
_java_thread_list=0x00007f7e58004bd0, length=11, elements={
0x00007f7f38023c90, 0x00007f7f38170d80, 0x00007f7f38172170, 0x00007f7f38177740,
0x00007f7f38178b00, 0x00007f7f38179f20, 0x00007f7f3817b8e0, 0x00007f7f3817ce20,
0x00007f7f38186290, 0x00007f7f38191980, 0x00007f7f381bec10
}
"main" #1 prio=5 os_prio=0 cpu=266723.38ms elapsed=272.87s tid=0x00007f7f38023c90 nid=0x25cf2 runnable [0x00007f7f3d5fd000]
java.lang.Thread.State: RUNNABLE
at Cpu_100.main(Cpu_100.java:7)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(java.base@17.0.12/Native Method)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(java.base@17.0.12/NativeMethodAccessorImpl.java:77)
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(java.base@17.0.12/DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(java.base@17.0.12/Method.java:568)
at com.sun.tools.javac.launcher.Main.execute(jdk.compiler@17.0.12/Main.java:419)
at com.sun.tools.javac.launcher.Main.run(jdk.compiler@17.0.12/Main.java:192)
at com.sun.tools.javac.launcher.Main.main(jdk.compiler@17.0.12/Main.java:132)
三、其他方式(非java程序)
pstack
采用pstack来查看某个进程的堆栈耿总情况,不需要附加到进程,因此不需要调试权限,只能显示当前对战的快照
lyl@localhost:~$ pstack 255949
Thread 23 (Thread 0x7f1184b3f6c0 (LWP 256100) "G1 Refine#1"):
#0 0x00007f11abdc39be in __futex_abstimed_wait_common () from /lib64/libc.so.6
#1 0x00007f11abdcefa0 in __new_sem_wait_slow64.constprop.0 () from /lib64/libc.so.6
#2 0x00007f11ab6b7622 in PosixSemaphore::wait() () from /usr/local/java/jdk-17.0.12/lib/server/libjvm.so
#3 0x00007f11ab0e00bc in G1ConcurrentRefineThread::run_service() () from /usr/local/java/jdk-17.0.12/lib/server/libjvm.so
#4 0x00007f11aafccb8b in ConcurrentGCThread::run() () from /usr/local/java/jdk-17.0.12/lib/server/libjvm.so
#5 0x00007f11ab7b4636 in Thread::call_run() () from /usr/local/java/jdk-17.0.12/lib/server/libjvm.so
#6 0x00007f11ab6038c1 in thread_native_entry(Thread*) () from /usr/local/java/jdk-17.0.12/lib/server/libjvm.so
#7 0x00007f11abdc711a in start_thread () from /lib64/libc.so.6
#8 0x00007f11abe37c3c in clone3 () from /lib64/libc.so.6
gdb
gdb来附加到该进程并查看其堆栈信息
gdb -p 255950
(gdb) info threads
(gdb) thread <n>
(gdb) bt
cat
Linux提供的接口,只显示主线程当前正在执行的内核栈
lyl@localhost:~$ cat /proc/255949/stack
[<0>] futex_wait_queue+0x65/0x90
[<0>] __futex_wait+0x151/0x1c0
[<0>] futex_wait+0x79/0x120
[<0>] do_futex+0xcb/0x190
[<0>] __x64_sys_futex+0x127/0x1e0
[<0>] do_syscall_64+0x7d/0x160
[<0>] entry_SYSCALL_64_after_hwframe+0x76/0x7e
strace
产看当前进程卡在那个系统调用
lyl@localhost:~$ strace -p 255949
strace: Process 255949 attached
futex(0x7f11aa951990, FUTEX_WAIT_BITSET|FUTEX_CLOCK_REALTIME, 255950, NULL, FUTEX_BITSET_MATCH_ANY
四、日志文件
其实出现任何问题无论是CPU和内存,都依赖强大的日志系统。像Linux的运行情况,都是会记录在/proc目录下面,日志文件大多数放在/var/log下面。如何中间件众多,比如大数据系统,如果没有日志系统,或者说不能良好的运用日志,出现问题,将寸步难行。还有一个比较重要的dump文件。dump文件用于存储系统崩溃时内存数据的文件。如果程序崩溃了,需要使用到dump文件。默认存在于/var/crash目录下面。
总结
线上的问题大部分都是CPU和内存综合来看去判断问题。CPU飙升,主要是因为上下文切换频繁、多线程、计算密集等原因。现在还有很多成熟的监控工具,基本出了问题就会以信息、邮件、电话的方式,提醒负责人。并将详细的堆栈信息展示出来。
- /proc是什么,为什么系统的信息要记录在proc中,这是一个文件系统吗?
- 在/proc目录下面,系统的信息是如何更新的,更新策略是怎样的
- 如果因为CPU飙升,导致程序崩溃了,重启了,如何去定位问题?当前的各种堆栈信息已经没了,系统都重启,当时问题暂时没办法再现了。
- 监控告警如何实现,脚本,定时任务,钉钉机器人等等?如果CPU飙升至100%,监控告警还能发送消息吗?
- 如何去调整某个进程的优先级,让监控报警优先执行。