💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
定位CPU的问题一般可以分为以下几个步骤: 1. 定位进程 2. 定位线程 3. 查看线程信息 4. 定位具体方法(代码) ### 定位进程 通过`top -c`(然后按`P`按cpu排序),`htop`等工具定位到具体的高CPU进程 假设定位到的进程ID为14279 或者使用jps查看java进程号 ### 定位线程 `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 ``` 一般超过80%就是比较高的,80%左右是合理情况 ### 查看线程信息 方法一:手动定位 使用jstack 分别找也上面的线程的具体内容,比如第一个线程 14293: ``` # 将线程ID转换为16进制 ``` printf '0x%x' 14293 => 0x37d5 ``` # 通过jstack查看进程中该线程的信息: `jstack pid | grep tid`找到线程堆栈 jstack 12816 | grep 0x3211 -A 30 # 【示例输出】 "VM Periodic Task Thread" os_prio=0 tid=0x00007ff1802d5800 nid=0x37d5 waiting on condition ``` 当然这里也可以直接使用`jstack 14279 > ~/tmp/pid-14279.log`显示所有线程,然后手动寻找对应的ID 当然更常见的是我们对整个jstack文件进行分析,通常我们会比较关注WAITING和TIMED\_WAITING的部分,BLOCKED就不用说了。我们可以使用命令`cat jstack.log | grep "java.lang.Thread.State" | sort -nr | uniq -c`来对jstack的状态有一个整体的把握,如果WAITING之类的特别多,那么多半是有问题啦 方法二:通过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) ``` 根据Arthas`thread`命令打印出来的堆栈信息定位具体的业务代码,review代码并尝试定位逻辑。如有必要, 还可以通过`watch`子命令监听某个方法的调用次数和资源占用情况 > 要注意的是,arthas的cpu占比,和前面两种cpu占比统计方式不同。前面两种针对的是Java进程启动开始到现在的cpu占比情况,arthas这种是一段采样间隔内,当前JVM里各个线程所占用的cpu时间占总cpu时间的百分比 ### 定位具体方法 1) GC线程 如果是GC线程一直在占用cpu,那么就基本确定是内存泄漏。进一步按照内存问题定位 2) 业务线程 如果是业务线程,那么根据下一节的方法,继续定位是哪些代码占用cpu * io wait * 比如磁盘空间不够导致的io阻塞 * 等待内核态锁,如 synchronized * `jstack -l pid | grep BLOCKED`查看阻塞态线程堆栈 * dump 线程栈,分析线程持锁情况 * arthas提供了`thread -b`,可以找出当前阻塞其他线程的线程。针对 synchronized 情况