性能调优--如何排查线上进程是否有死锁?

1,841 阅读3分钟

「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战

Jstack可以用来查看堆栈使用情况,以及进程死锁情况。下面就来看看如何排查进程死锁

还是通过案例来分析

package com.lxl.jvm;
​
public class DeadLockTest {
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();
​
    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (lock1) {
                try {
                    System.out.println("thread1 begin");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
​
                }
                synchronized (lock2) {
                    System.out.println("thread1 end");
                }
            }
        }).start();
​
        new Thread(() -> {
            synchronized (lock2) {
                try {
                    System.out.println("thread2 begin");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
​
                }
                synchronized (lock1) {
                    System.out.println("thread2 end");
                }
            }
        }).start();
    }
}

上面是两把锁,互相调用。

  1. 定义了两个成员变量lock1,lock2

  2. main方法中定义了两个线程。

    • 线程1内部使用的是同步执行--上锁,锁是lock1。休眠5秒钟之后,他要获取第二把锁,执行第二段代码。
    • 线程2和线程1类似,锁相反。
  3. 问题:一开始,像个线程并行执行,线程一获取lock1,线程2获取lock2.然后线程1继续执行,当休眠5s后获取开启第二个同步执行,锁是lock2,但这时候很可能线程2还没有执行完,所以还没有释放lock2,于是等待。线程2刚开始获取了lock2锁,休眠五秒后要去获取lock1锁,这时lock1锁还没释放,于是等待。两个线程就处于相互等待中,造成死锁。

第一步:通过Jstack命令来看看是否能检测到当前有死锁。

jstack 51789

img

从这里面个异常可以看出,

  • prio:当前线程的优先级
  • cpu:cpu耗时
  • os_prio:操作系统级别的优先级
  • tid:线程id
  • nid:系统内核的id
  • state:当前的状态,BLOCKED,表示阻塞。通常正常的状态是Running我们看到Thread-0和Thread-1线程的状态都是BLOCKED.

通过上面的信息,我们判断出两个线程的状态都是BLOCKED,可能有点问题,然后继续往下看。

img

我们从最后的一段可以看到这句话:Found one Java-level deadlock; 意思是找到一个死锁。死锁的线程号是Thread-0,Thread-1。

Thread-0:正在等待0x000000070e706ef8对象的锁,这个对象现在被Thread-1持有。

Thread-1:正在等待0x000000070e705c98对象的锁,这个对象现在正在被Thread-0持有。

最下面展示的是死锁的堆栈信息。死锁可能发生在DeadLockTest的第17行和第31行。通过这个提示,我们就可以找出死锁在哪里了。

第二步:使用jvisualvm查看死锁

如果使用jstack感觉不太方便,还可以使用jvisualvm,通过界面来查看,更加直观。

在程序代码启动的过程中,打开jvisualvm工具。

img

找到当前运行的类,查看线程,就会看到最头上的一排红字:检测到死锁。然后点击“线程Dump”按钮,查看相信的线程死锁的信息。

img

这里可以找到线程私锁的详细信息,具体内容和上面使用Jstack命令查询的结果一样,这里实用工具更加方便。