Java应用问题定位系列——CPU消耗过高

定位CPU的问题一般可以分为以下几个步骤:

  1. 定位进程
  2. 定位线程
  3. 查看线程信息
  4. 定位具体方法(代码)

一、定位进程

通过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% 排查优化实践
image

你可能感兴趣的:(java,问题解决,cpu过高)