CPU消耗过高
常见的消耗CPU场景
- 频繁GC,访问量高时,有可能造成频繁的GC、甚至FGC。当调用量大时,内存分配过快,就会造成GC线程不停的执行,导致CPU飙高
- 序列化与反序列化,调用量增大的情况下,导致了CPU被打满
- 加密、解密
- 正则表达式校验,Java 正则表达式使用的引擎实现是 NFA 自动机,这种引擎在进行字符匹配会发生回溯(backtracking)
- 线程上下文切换、当启动了很多线程,而这些线程都处于不断的阻塞状态(锁等待、IO等待等)和执行状态的变化过程中。当锁竞争激烈时,很容易出现这种情况
- 某些线程在做无阻塞的运算,简单的例子while(true)中不停的做运算,没有任何阻塞。写程序时,如果需要做很久的计算,可以适当将程序sleep下
- while(true)导致CPU飙升,死循环会调用 CPU 寄存器进行计数,死循环不会让出 CPU,除非操作系统时间片到期,但死循环会不断向系统申请时间片,直到系统没有空闲时间做别的事情。
- 频繁 Young GC引起 CPU 使用率飚升,Young GC 本身是 JVM 进行垃圾回收的操作,会计算内存和调用寄存器
- 线程数很高的应用,不一定影响CPU使用率,如果大多数处于等待状态,Runable 和 Running 状态的线程不多,这时 CPU 使用率不一定会高。
排查问题
1. 执行 top -c 命令,找到 cpu 最高的进程的 id
top -c
2. jstack PID 导出 Java 应用程序的线程堆栈信息
jstack 15244
在打印的堆栈日志文件中,tid 和 nid 的含义:
- nid : 对应的 Linux 操作系统下的 tid 线程号,也就是前面转化的 16 进制数字
- tid: 这个应该是 jvm 的 jmm 内存规范中的唯一地址定位
在 CPU 过高的情况下
- 查找响应的线程,一般定位都是用 nid 来定位的。
- 如果发生死锁之类的问题,一般用 tid 来定位。
3. 定位 CPU 高的线程打印其 nid
top -H -p 23395
[root@test03 ~]# top -H -p 23395
top - 17:29:56 up 128 days, 3:59, 5 users, load average: 0.39, 0.29, 0.32
Threads: 112 total, 0 running, 112 sleeping, 0 stopped, 0 zombie
%Cpu(s): 7.2 us, 1.9 sy, 0.0 ni, 90.7 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 st
KiB Mem : 8011232 total, 135016 free, 7255296 used, 620920 buff/cache
KiB Swap: 4194300 total, 145072 free, 4049228 used. 478568 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
15294 root 20 0 4733844 693016 13952 S 1.0 8.7 0:08.90 java
15303 root 20 0 4733844 693016 13952 S 1.0 8.7 0:10.61 java
15306 root 20 0 4733844 693016 13952 S 0.7 8.7 0:09.72 java
15435 root 20 0 4733844 693016 13952 S 0.7 8.7 0:00.97 java
15309 root 20 0 4733844 693016 13952 S 0.3 8.7 0:03.90 java
15380 root 20 0 4733844 693016 13952 S 0.3 8.7 0:01.02 java
15388 root 20 0 4733844 693016 13952 S 0.3 8.7 0:01.10 java
15389 root 20 0 4733844 693016 13952 S 0.3 8.7 0:00.99 java
15396 root 20 0 4733844 693016 13952 S 0.3 8.7 0:01.03 java
15398 root 20 0 4733844 693016 13952 S 0.3 8.7 0:00.99 java
15416 root 20 0 4733844 693016 13952 S 0.3 8.7 0:01.11 java
15430 root 20 0 4733844 693016 13952 S 0.3 8.7 0:01.01 java
15244 root 20 0 4733844 693016 13952 S 0.0 8.7 0:00.00 java
15245 root 20 0 4733844 693016 13952 S 0.0 8.7 0:19.88 java
15246 root 20 0 4733844 693016 13952 S 0.0 8.7 0:00.66 java
15247 root 20 0 4733844 693016 13952 S 0.0 8.7 0:00.60 java
15248 root 20 0 4733844 693016 13952 S 0.0 8.7 0:00.63 java
由此可以看出占用 CPU 较高的线程,但是这些还不够,无法直接定位到具体的类
nid 是 16 进制的,所以我们要获取线程的 16 进制 ID:
[root@test03 ~]# printf "%x\n" 23395
5b63
然后根据输出结果到 jstack 打印的堆栈日志中查定位:
"Abandoned connection cleanup thread" daemon prio=10 tid=0x00007f05d8031000 nid=0x5b63 in Object.wait() [0x00007f06442fa000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
- locked <0x00000000c2f83560> (a java.lang.ref.ReferenceQueue$Lock)
at com.mysql.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:43)
CLC