当系统CPU占用超过正常水平时,需要找出是哪个进程、哪个线程以及原因,以便找出解决的办法。
1. 采用top命令找出占用CPU最高的进程
# top
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
12625 root 20 0 3473404 859584 7484 S 4.0 10.7 153:03.11 java
13428 root 20 0 3190116 433504 7560 S 1.3 5.4 53:48.25 java
13025 root 20 0 3250076 520304 7624 S 0.3 6.5 32:37.20 java
可以看出这里CPU占用最高的12625
2. 执行命令查看进程内的线程列表
# top -Hp 12625
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
12699 root 20 0 3473404 860376 7484 S 4.3 10.7 3:37.49 java
12702 root 20 0 3473404 860376 7484 S 4.3 10.7 3:37.86 java
12764 root 20 0 3473404 860376 7484 S 2.2 10.7 3:18.46 java
12913 root 20 0 3473404 860376 7484 S 2.2 10.7 1:41.47 java
12625 root 20 0 3473404 860376 7484 S 0.0 10.7 0:00.00 java
可以看到这里占用CPU最高的是PID是12699,此时的PID是线程ID
top命令显示界面shift+p 按cpu排序,shift+m 按内存排序。
3. 将上一步得到的线程ID转换为16进制数
printf '%x\n' 12699
319b
这里需要使用小写字母的十六进制。
4. 通过jstack查看线程堆栈信息
# jstack 12625 | grep -A 10 319b
"http-nio-8083-exec-10" #50 daemon prio=5 os_prio=0 tid=0x00007f62f5537000 nid=0x319b waiting on condition [0x00007f629e7ea000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000e5d8aa18> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:108)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
注意,这里的-A 10表示查找到所在行的后10行。
通过对线程堆栈信息的查看,可以判断出线程执行的大致任务,如果是垃圾回收线程占用了太多的CPU,则说明产生了大量的对象,快速消耗了内存,此时需要进一步的分析其他的线程,查看产生大量对象的具体原因是什么。
如果是业务线程占用CPU高,则需要分析是否确实是业务请求量大,如果是这样,则需要首先需要考虑是否可以进行业务处理优化,如果没有优化空间,就需要考虑增加配置,对系统进行升级扩充了。
5. 导出线程堆栈到文件
为了对整个进程的线程进行分析,可以导出所有线程的堆栈。
jstack 12625 > stack.dump
通过对各个线程堆栈的分析,可以发现线程重复执行的任务集中在什么地方。结合业务和源码的分析,可以判断出问题的原因,找出优化及解决的思路。