使用Thread Dump诊断死锁和性能瓶颈问题

场景:Java程序在运行的过程中出现了死锁或者运行的很慢,查看日志也没发现什么Exception,该如何解决呢?

  1. 一段死锁程序:

    public class DeadLock {
    
        public static Object lockA = new Object();
        public static Object lockB = new Object();
    
        public static void main(String[] args) {
            new ThreadA().start();
            new ThreadB().start();
        }
    }
    
    class ThreadA extends Thread {
    
        @Override
        public void run() {
            synchronized(DeadLock.lockA) {
                System.out.println(Thread.currentThread().getName() + " get LockA");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized(DeadLock.lockB) {
                    System.out.println(Thread.currentThread().getName() + " get LockB");
                }
            }
        }
    }
    
    class ThreadB extends Thread {
    
        @Override
        public void run() {
            synchronized(DeadLock.lockB) {
                System.out.println(Thread.currentThread().getName() + " get LockB");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized(DeadLock.lockA) {
                    System.out.println(Thread.currentThread().getName() + " get LockA");
                }
            }
        }
    }
  2. 运行该程序会出现死锁:
    在这里插入图片描述

  3. 使用JDK提供的一个非常强大的性能诊断工具:Thread Dump来解决。

    3.1. 在Linux系统上通过执行kill -3 PID来获取某个java进程的Thread Dump信息。
    3.2. 使用JDK自带的工具:jps 来获取执行中的java进程的PID。
    3.3. 新开一个命令行窗口,执行jps获取运行中的java进程的PID。
    在这里插入图片描述
    3.4. 紧接着执行kill -3 1529命令,我们就会发现在原来发生死锁的那个命令行窗口中出现了如下信息(Thread Dump信息):
    Thread Dump
    3.5. 详细的信息如下:

    2018-11-10 11:55:08
    Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode):
    
    "DestroyJavaVM" #10 prio=5 os_prio=0 tid=0x00007f4a74008800 nid=0x5fa waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Thread-1" #9 prio=5 os_prio=0 tid=0x00007f4a740ca800 nid=0x604 waiting for monitor entry [0x00007f4a78808000]
       java.lang.Thread.State: BLOCKED (on object monitor)
    	at ThreadB.run(DeadLock.java:43)
    	- waiting to lock <0x00000000e0e5ddc8> (a java.lang.Object)
    	- locked <0x00000000e0e5ddd8> (a java.lang.Object)
    
    "Thread-0" #8 prio=5 os_prio=0 tid=0x00007f4a740c8800 nid=0x603 waiting for monitor entry [0x00007f4a78909000]
       java.lang.Thread.State: BLOCKED (on object monitor)
    	at ThreadA.run(DeadLock.java:25)
    	- waiting to lock <0x00000000e0e5ddd8> (a java.lang.Object)
    	- locked <0x00000000e0e5ddc8> (a java.lang.Object)
    
    "Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007f4a740b3000 nid=0x601 runnable [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f4a740b0000 nid=0x600 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f4a740ae000 nid=0x5ff waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f4a740ac800 nid=0x5fe waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f4a74079800 nid=0x5fd in Object.wait() [0x00007f4a78f0f000]
       java.lang.Thread.State: WAITING (on object monitor)
    	at java.lang.Object.wait(Native Method)
    	- waiting on <0x00000000e0e08ec8> (a java.lang.ref.ReferenceQueue$Lock)
    	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
    	- locked <0x00000000e0e08ec8> (a java.lang.ref.ReferenceQueue$Lock)
    	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
    	at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
    
    "Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f4a74074800 nid=0x5fc in Object.wait() [0x00007f4a79010000]
       java.lang.Thread.State: WAITING (on object monitor)
    	at java.lang.Object.wait(Native Method)
    	- waiting on <0x00000000e0e06b68> (a java.lang.ref.Reference$Lock)
    	at java.lang.Object.wait(Object.java:502)
    	at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
    	- locked <0x00000000e0e06b68> (a java.lang.ref.Reference$Lock)
    	at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
    
    "VM Thread" os_prio=0 tid=0x00007f4a7406d000 nid=0x5fb runnable 
    
    "VM Periodic Task Thread" os_prio=0 tid=0x00007f4a740b6800 nid=0x602 waiting on condition 
    
    JNI global references: 6
    
    
    Found one Java-level deadlock:
    =============================
    "Thread-1":
      waiting to lock monitor 0x00007f4a580062c8 (object 0x00000000e0e5ddc8, a java.lang.Object),
      which is held by "Thread-0"
    "Thread-0":
      waiting to lock monitor 0x00007f4a58004e28 (object 0x00000000e0e5ddd8, a java.lang.Object),
      which is held by "Thread-1"
    
    Java stack information for the threads listed above:
    ===================================================
    "Thread-1":
    	at ThreadB.run(DeadLock.java:43)
    	- waiting to lock <0x00000000e0e5ddc8> (a java.lang.Object)
    	- locked <0x00000000e0e5ddd8> (a java.lang.Object)
    "Thread-0":
    	at ThreadA.run(DeadLock.java:25)
    	- waiting to lock <0x00000000e0e5ddd8> (a java.lang.Object)
    	- locked <0x00000000e0e5ddc8> (a java.lang.Object)
    
    Found 1 deadlock.
    
    Heap
     def new generation   total 9792K, used 1393K [0x00000000e0e00000, 0x00000000e18a0000, 0x00000000eb400000)
      eden space 8704K,  16% used [0x00000000e0e00000, 0x00000000e0f5c560, 0x00000000e1680000)
      from space 1088K,   0% used [0x00000000e1680000, 0x00000000e1680000, 0x00000000e1790000)
      to   space 1088K,   0% used [0x00000000e1790000, 0x00000000e1790000, 0x00000000e18a0000)
     tenured generation   total 21888K, used 0K [0x00000000eb400000, 0x00000000ec960000, 0x0000000100000000)
       the space 21888K,   0% used [0x00000000eb400000, 0x00000000eb400000, 0x00000000eb400200, 0x00000000ec960000)
     Metaspace       used 2485K, capacity 4490K, committed 4864K, reserved 1056768K
      class space    used 269K, capacity 386K, committed 512K, reserved 1048576K

    可以看到这里发现了一个死锁:

    “Thread-1”:
    at ThreadB.run(DeadLock.java:43)
    - waiting to lock <0x00000000e0e5ddc8> (a java.lang.Object)
    - locked <0x00000000e0e5ddd8> (a java.lang.Object)
    “Thread-0”:
    at ThreadA.run(DeadLock.java:25)
    - waiting to lock <0x00000000e0e5ddd8> (a java.lang.Object)
    - locked <0x00000000e0e5ddc8> (a java.lang.Object)

    Found 1 deadlock.

    (1). Thread-1在等待锁住<0x00000000e0e5ddc8>,但是Thread-0已经锁住了<0x00000000e0e5ddc8>。
    (2). 分别发生在DeadLock.java的43行和25行,定位到代码对应的位置:

    13 class ThreadA extends Thread {
         14 
         15     @Override
         16     public void run() {
         17         synchronized(DeadLock.lockA) {
         18             System.out.println(Thread.currentThread().getName() + " get LockA");
         19             try {
         20                 Thread.sleep(1000);
         21             } catch (InterruptedException e) {
         22                 e.printStackTrace();
         23             }
         24             synchronized(DeadLock.lockB) {
         25                 System.out.println(Thread.currentThread().getName() + " get LockB");
         26             }
         27         }
         28     }
         29 }
    31 class ThreadB extends Thread {
         32 
         33     @Override
         34     public void run() {
         35         synchronized(DeadLock.lockB) {
         36             System.out.println(Thread.currentThread().getName() + " get LockB");
         37             try {
         38                 Thread.sleep(1000);
         39             } catch (InterruptedException e) {
         40                 e.printStackTrace();
         41             }
         42             synchronized(DeadLock.lockA) {
         43                 System.out.println(Thread.currentThread().getName() + " get LockA");
         44             }
         45         }
         46     }
         47 }

    发现两个线程分别在持有了各自的锁的前提下去抢夺对方的锁,结果陷入死锁,使用kill -9 1529杀掉死锁进程,修改代码,重新部署到服务器,问题解决。

  4. 另外,正常运行的java线程的状态是RUNNABLE,陷入阻塞或者有问题的线程将处于BLOCKED状态,根据Threa Dump信息也可以看出来。