CPU和内存的排查思路是一样的。
问题模拟代码
先写一段模拟代码,模拟CPU和内存100%的过程
public class PressureTest implements Runnable{ @Override public void run() { while (true) { } } }
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import java.util.LinkedList; import java.util.List; /** * <p color="orange"> * 线上问题排查 * </p> * * @author BookSea * @version 1.0 * @since 2022/3/27 12:56 */ @RestController public class ProblemController { /** * <p color='orange'> * 模拟CPU飙高异常 * 其中num是计算机的逻辑处理器个数 * </p> * * @since 2022/3/27 12:58 */ @GetMapping("/cpu/{id}") public void cpu(@PathVariable("id") Integer num) { System.out.println("----CPU100%---"); System.out.println("启动" + num + "个线程"); Thread[] threads = new Thread[num]; for (int i = 0; i < num; i++) { threads[i] = new Thread(new PressureTest()); threads[i].start(); } } }
打包部署到服务器。并使用nohup java -jar &运行。
问题复现
这是没请求之前整个系统的状态
系统正常,shift+p可以按照CPU排序,shift+m可以按照内存排序
先请求下接口
curl http://localhost:8080/cpu/8
此时再看下系统的状态
top -c
可以看到CPU使用率100%直接被打满了。
找到CPU使用率最高的进程 16253,使用
top -Hp 16253
查看进程里面的线程使用情况。
可以看到使用率最高的是8个线程,因为我们请求的时候一共请求了8个线程
线程id分别是:16405~16412
先使用
jstack -l 16253 > ./16253.tdump
导出线程的快照情况。
Dump文件解读
首先,先对我们dump的文件进行一个解读
首先第一行显示的是dump的时间,第二行是虚拟机的一些信息,接着就是线程的list,包括每个线程的tid。
这里参考了一张其他博主的图:转载地址
做几点补充:
- 如果出现daemon说明是守护线程。
- prio 和 os_prio分别是线程jvm优先级,线程操作系统优先级。
- tid是jvm线程id,jvm内部线程的唯一标识(可以通过java.lang.Thread.getId()获取)
- nid是对应系统线程id(NativeThread ID),和top命令查看的线程pid对应,不过一个是10进制,一个是16进制。
- 线程状态是我们要重点关注的,有如下状态:
推荐一篇文章:学会查看jstack Dump 日志
问题排查
用计算器把线程id转为16进制。
进行查找
发现线程id 16405~16412 都在PressureTest.java:17这个位置
定位到代码
我们知道发生了死循环的问题。
工具推荐
自己查看快照信息的话比较不方便,网上有专门分析的平台。
这里推荐3个分析网站,这3个是同一家公司的。
线程Dump分析:http://fastthread.io/
GC日志分析:https://gceasy.io/
Heap Dump分析:https://heaphero.io/
直接上传快照文件就可以,效果是这样的。