jstack是用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内存每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因。
一、分析CPU占用高的问题
在实际项目开发过程中,CPU占用异常是经常遇到的问题,当遇到CPU异常时,我们可以借助jstack进行问题排查。
下面我们通过模拟,刨析一下整个问题分析过程。
模拟代码
在下面的代码中,我们写了一个死循环,模拟CPU占用过高问题。
public class ThreadMain {
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;
}
}
}
}
}查询CPU占用高的进程
在Linux服务器,我们通过top命令,可以查看CPU信息,默认按照由高到低显示。
输入top命令,查询进程信息。
top
可以看到,目前占用最高的进程ID是9420
查询对应线程
在上面,我们已经查询到9420进程,我们可以进一步分析,9420进程内到底是哪个线程CPU占用最高。
输入一下命令,可以查看线程信息
top Hp 9420
可以看到目前占用CPU最高的线程是9445
线程转换
我们通过top命令查询到的9445线程ID是10进制的,但是jstack展示的线程是16进制的,因此我们需要将10进制的9445转成16进制,也就是0x24e5
通过jstack查询线程快照
输入一下命令
jstack 9420
jstack输出信息的nid就是16进制的线程id,我们找到nid是0x24e5线程,可以定位到具体代码位置 at ThreadMain$Task.run(ThreadMain.java:30),也就是ThreadMain.java的第30行。
二、分析线程死锁问题
模拟代码
我们通过下面的代码,模拟线程死锁问题
public class ThreadMain {
private final static Object lockA = new Object();
private final static Object lockB = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + "获得了锁LockA");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
try {
throw new RuntimeException(e);
} catch (RuntimeException ex) {
throw new RuntimeException(ex);
}
}
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + "获得了锁LockB");
}
}
}).start();
new Thread(() -> {
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + "获得了锁LockB");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
try {
throw new RuntimeException(e);
} catch (RuntimeException ex) {
throw new RuntimeException(ex);
}
}
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + "获得了锁LockA");
}
}
}).start();
}
}查询VMID
我们通过jps命令,可以查询虚拟机进程ID
jps -l
可以看到,我们当前VMID是18395
查询线程信息
同样通过jstack命令可以查询进程信息。
jstack 18395
重点关注状态时BLOCKED的线程,我们可以看到Thread-1跟Thread-1目前都是BLOCKED状态,Thread-1目前锁定了<0x0000000782539600>并且正在等待<0x00000007825395f0>,而Thread-0正好相反,Thread-0锁定了<0x00000007825395f0>并且正在等待<0x0000000782539600>,这就导致两个线程相互锁定。当然,我们也可以看到两个线程异常发生的位置。
评论 (0)