使用场景
当我们的线上程序 CPU 飙高的时候、线程发生死锁的时候等,我们需要排查这些情况,那如何查看进程里的线程信息呢?这个时候需要将进程内的线程运行状态进行转储,好进行分析,这个时候就需要使用 jstack
进行分析
基本用法
Usage:
jstack [-l] <pid>
(to connect to running process)
jstack -F [-m] [-l] <pid>
(to connect to a hung process)
jstack [-m] [-l] <executable> <core>
(to connect to a core file)
jstack [-m] [-l] [server_id@]<remote server IP or hostname>
(to connect to a remote debug server)
Options:
-F to force a thread dump. Use when jstack <pid> does not respond (process is hung)
-m to print both java and native frames (mixed mode)
-l long listing. Prints additional information about locks
-h or -help to print this help message
基本用法写的很清楚
jstack操作演示
下面以 tomcat 演示一下基本的操作过程。
我首先打开一个 tomcat 进程并启动,使用 jps -l
查看正在运行的 java 进程信息
可以看到 tomcat 所在的进程 id 为 17408,接着使用命令jstack 17408 > 17408.txt
生成一个文件,使用文本编辑器打开
几个参数说明:#40
这表示线程编号,daemon
表示这是守护线程,prio=5 os_prio=0
表示线程的优先级以及系统的优先级,java.lang.Thread.State: RUNNABLE
表示这个线程处于什么状态
文件中还有许多其他的线程,包括 GC 线程,main 线程等等
jstack实战死锁
这一节我们通过手动构造一个死锁,然后使用 jstack 进行排查
@RestController
@RequestMapping("/cpu")
public class CpuController {
private Object lock1 = new Object();
private Object lock2 = new Object();
@RequestMapping("/deadlock")
public String deadLock() {
new Thread(() -> {
synchronized (lock1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("lock1获取lock2成功");
}
}
}).start();
new Thread(() -> {
synchronized (lock2) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("lock2获取lock1成功");
}
}
}).start();
return "deadlock";
}
}
跑起来之后使用 jstack 转储
打开文件拖到最后如下所示
可以发现,jstack 自动发现了一个死锁,并且定位到了死锁的位置,通过查看看信息可以知道, waiting to lock <0x00000000ff00bce8>
,可以很轻松的定位到死锁的位置。这是我们手动构造的,所以就算不用 jstack 也很容易知道问题,但是实际项目代码量很多,如果依靠自己去排查,难度是相当高的,所以借助 jstack 能够很轻松的知道问题所在
jstack实战死循环
使用 jstack 的另一种场景就是死循环的发生,导致的直接后果就是 CPU 利用率飙高,一旦使用率飙高之后,就会阻塞其他的访问。这里我提供一个思路,由于我是在 Windows下跑的,Linux的命令无法使用,只能使用资源管理器进行查看,代码也很简单,我在访问的函数接口中构造一个死循环,当访问这个接口的时候就会死循环,我开了四个窗口访问这个接口,结果上使用资源管理器查看 CPU 已经跑满 100% 了,那么在 Linux 下该如何定位这个问题呢?
首先查看正在跑的程序的 pid,然后使用 top 命令进行查看top -p pid H
,前几个就是 CPU占用率特别高的,记住前面的 pid数字,转成 16 进制,因为 jstack 内部表示 pid是使用 16 进制表示的。然后发开转储文件,搜索对应的 pid 号,查看具体的堆栈信息,里面包含了函数调用情况,一般来说能够大致的解决问题。
这里先留个坑,Linux下的死循环问题等以后补上
总结
这里简单的介绍了 jstack 的使用情况,以及简单的案例进行分析 jstack 是如何定位死锁以及死循环的问题,在实际中可以多多的进行模拟分析。