概述
jstack命令用于打印指定Java进程、核心文件或远程调试服务器的Java线程堆栈的跟踪信息。换句话说,就是jstack能生成JVM当前时刻的线程快照,以此来定位线程出现长时间停顿的原因,最常见应用场景为:
- 查看线程间死锁
- 分析CPU过高原因
针对这两个场景,后续会有实战案例~
常用指令
jstack [-option] <pid> // 打印某个进程的堆栈信息
其他常用指令如下:
指令 | 说明 |
---|---|
-F | 当jstack指令无响应时,强制打印一个堆栈信息 |
-m | 打印包含Java和C/C++帧的混合模式堆栈跟踪 |
-l | 打印关于锁的其他信息,比如拥有java.util.concurrent ownable同步器的列表 |
-h / -help | 打印帮助信息 |
Jstack实战
案例一:jstack分析死锁
public class DeadLock {
private static Object lockA = new Object();
private static Object lockB = new Object();
public static void main(String[] args) {
new Thread(() ->{
synchronized (lockA) {
try {
System.out.println("线程1开始运行========");
Thread.sleep(2000);
} catch (InterruptedException e) {
}
synchronized (lockB) {
System.out.println("线程1运行结束========");
}
}
}).start();
new Thread(() ->{
synchronized (lockB) {
try {
System.out.println("线程2开始运行========");
Thread.sleep(2000);
} catch (InterruptedException e) {
}
synchronized (lockA) {
System.out.println("线程2结束运行========");
}
}
}).start();
System.out.println("主线程运行结束========");
}
}
运行结果如下:
线程1开始运行========
主线程运行结束========
线程2开始运行========
可见,线程1和线程2都没有运行完成,两个线程在抢夺资源过程中陷入了死循环
排查思路
- 使用jps指令查看当前运行中的java程序
- 使用jstack -l < pid > ,输出线程及锁详情
- 进行死锁分析
动手实践
那么首先使用jps:
使用jstack指令:
通过上图,可以得到死锁的信息:
"Thread-1":
waiting to lock monitor 0x000000001d290de8 (object 0x000000076b19b890, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x000000001d293678 (object 0x000000076b19b8a0, a java.lang.Object),
which is held by "Thread-1"
并且可以定位到对应代码位置,是不是非常的方便~
案例二:jstack分析CPU过高
实例代码如下:
public class CpuProblem {
private static ExecutorService executorService = Executors.newFixedThreadPool(5);
public static void main(String[] args) {
Task task1 = new Task();
Task task2 = new Task();
executorService.execute(task1);
executorService.execute(task2);
}
public static Object lock = new Object();
static class Task implements Runnable {
public void run() {
synchronized (lock) {
long sum = 0L;
while (true) {
sum += 1;
}
}
}
}
}
排查思路
1. top
2. top -Hp <pid>
3. 获取cpu最高的进程号并转换为16进制<pid_16>
4. jstack <pid>|grep -A 10 <pid_16>
5. 查看对应问题的代码
动手实践
-
使用命令top -p ,显示你的java进程的内存情况,pid是你的java进程号,比如19663
-
按H(获取top -Hp pid指令),获取每个线程的内存情况
-
找到内存和cpu占用最高的线程tid,比如19664
-
转为十六进制得到 0x4cd0,此为线程id的十六进制表示
-
执行 jstack 19663|grep -A 10 4cd0,得到线程堆栈信息中 4cd0 这个线程所在行的后面10行,从堆栈中可以发现导致cpu飙高的调 用方法
-
查看对应的堆栈信息找出可能存在问题的代码