定位CPU的问题一般可以分为以下几个步骤:
- 定位进程
- 定位线程
- 查看线程信息
- 定位具体方法(代码)
一、定位进程
通过top -c
(然后按P
按cpu排序),htop
等工具定位到具体的高CPU进程。
假设定位到的进程ID为14279。
二、定位线程
top -H -p 14279
定位占cpu的线程:
%Cpu(s): 0.5 us, 0.7 sy, 0.0 ni, 98.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 8168236 total, 231696 free, 3660496 used, 4276044 buff/cache
KiB Swap: 969964 total, 969964 free, 0 used. 4197860 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
14293 faceless 20 0 4508772 97036 18112 S 45 1.2 152:35.42 java
14279 faceless 20 0 4508772 97036 18112 S 23 1.2 0:00.00 java
14282 faceless 20 0 4508772 97036 18112 S 0.0 1.2 0:00.37 java
三、查看线程信息
方法一:手动定位
使用jstack 分别找也上面的线程的具体内容,比如第一个线程 14293:
# 将线程ID转换为16进制
printf '%x\n' 14293
# 【示例输出】
37d5
# 通过jstack查看进程中该线程的信息:
jstack 14279 | grep 37d5
# 【示例输出】
"VM Periodic Task Thread" os_prio=0 tid=0x00007ff1802d5800 nid=0x37d5 waiting on condition
当然这里也可以直接使用jstack 14279 > ~/tmp/pid-14279.log
显示所有线程,然后手动寻找对应的ID。
方法二,通过arthas定位
Arthas支持直接通过thread
子命令显示占用cpu最高的n个线程。
展示当前最忙的前3个线程并打印堆栈:
$ thread -n 3
"as-command-execute-daemon" Id=29 cpuUsage=75% RUNNABLE
at sun.management.ThreadImpl.dumpThreads0(Native Method)
at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:440)
at com.taobao.arthas.core.command.monitor200.ThreadCommand$1.action(ThreadCommand.java:58)
at com.taobao.arthas.core.command.handler.AbstractCommandHandler.execute(AbstractCommandHandler.java:238)
at com.taobao.arthas.core.command.handler.DefaultCommandHandler.handleCommand(DefaultCommandHandler.java:67)
at com.taobao.arthas.core.server.ArthasServer$4.run(ArthasServer.java:276)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Number of locked synchronizers = 1
- java.util.concurrent.ThreadPoolExecutor$Worker@6cd0b6f8
"as-session-expire-daemon" Id=25 cpuUsage=24% TIMED_WAITING
at java.lang.Thread.sleep(Native Method)
at com.taobao.arthas.core.server.DefaultSessionManager$2.run(DefaultSessionManager.java:85)
"Reference Handler" Id=2 cpuUsage=0% WAITING on java.lang.ref.Reference$Lock@69ba0f27
at java.lang.Object.wait(Native Method)
- waiting on java.lang.ref.Reference$Lock@69ba0f27
at java.lang.Object.wait(Object.java:503)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
详细参考:https://alibaba.github.io/art...
然后根据线程决定下一步动作:
1) GC线程
如果是GC线程一直在占用cpu,那么就基本确定是内存泄漏。进一步按照内存问题定位。
可以参考这篇博文:https://my.oschina.net/crosso...
2) 业务线程
如果是业务线程,那么根据下一节的方法,继续定位是哪些代码占用cpu。
四、定位具体方法
1. 根据堆栈信息排查
根据jstack
或者Arthas thread
命令打印出来的堆栈信息定位具体的业务代码,review代码并尝试定位逻辑。如有必要,
还可以通过watch
子命令监听某个方法的调用次数和资源占用情况。
可以参考:一次用arthas定位akka的CPU占用过高的经历
2. 通过火焰图分析
通过火焰图(flame graph)方法,参考:https://my.oschina.net/jijunj... 。
3. 通过dump快照,然后通过http://fastthread.io/分析
可以参考这边博文:一次生产 CPU 100% 排查优化实践
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。