zoukankan      html  css  js  c++  java
  • Java线程堆栈分析

    线程堆栈也称作线程调用堆栈。Java线程堆栈是虚拟机中线程(包括锁)状态的一个瞬间快照,即系统在某个时刻所有线程的运行状态,包括每一个线程的调用堆栈,锁的持有情况等信息。对于已经消失而又没留有痕迹的信息,线程堆栈是无法进行历史追踪的。
    线程堆栈的信息包括:(1)线程的名字,id,线程的数量等(2)线程的运行状态,锁的状态(锁被哪个线程持有,哪个线程再等待锁等)(3)调用堆栈(即函数的调用层次关系),调用堆栈包括完整的类名,所执行的方法,源代码的行数。
    借助线程堆栈可以分析:线程死锁、锁争用、死循环、识别耗时操作、稳定性分析和性能分析等问题。

    1. 打印线程堆栈

    Java虚拟机提供了线程转储(Thread dump)的后门。通过如下的命令行方式向Java进程请求堆栈输出:
    window 在运行Java的控制窗口上按ctrl + break组合键
    linux 使用kill -3 pid

    2. 解读线程堆栈

    2.1 线程的解读

    Java源代码

    public class MyTest {
        Object obj1 = new Object();
    
        Object obj2 = new Object();
    
        public void fun1() {
            synchronized (obj1) {
                fun2();
            }
        }
    
        public void fun2() {
            synchronized (obj2) {
                while (true) {
                }
            }
        }
    
        public static void main(String[] args) {
            MyTest mt = new MyTest();
            mt.fun1();
        }
    }

    编译运行,kill -3 pid

    2019-05-30 14:27:14
    Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.111-b14 mixed mode):
    
    "Service Thread" #17 daemon prio=9 os_prio=0 tid=0x00007f41680f5800 nid=0x562e runnable [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C1 CompilerThread11" #16 daemon prio=9 os_prio=0 tid=0x00007f41680f2800 nid=0x562d waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C1 CompilerThread10" #15 daemon prio=9 os_prio=0 tid=0x00007f41680f0800 nid=0x562c waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C1 CompilerThread9" #14 daemon prio=9 os_prio=0 tid=0x00007f41680ee000 nid=0x562b waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C1 CompilerThread8" #13 daemon prio=9 os_prio=0 tid=0x00007f41680ec000 nid=0x562a waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread7" #12 daemon prio=9 os_prio=0 tid=0x00007f41680ea000 nid=0x5629 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread6" #11 daemon prio=9 os_prio=0 tid=0x00007f41680e8000 nid=0x5628 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread5" #10 daemon prio=9 os_prio=0 tid=0x00007f41680e5800 nid=0x5627 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread4" #9 daemon prio=9 os_prio=0 tid=0x00007f41680db800 nid=0x5626 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread3" #8 daemon prio=9 os_prio=0 tid=0x00007f41680d9800 nid=0x5625 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00007f41680d7000 nid=0x5624 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f41680d5800 nid=0x5623 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f41680d2800 nid=0x5622 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f41680d1000 nid=0x5621 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f416809e000 nid=0x5620 in Object.wait() [0x00007f4144b7a000]
       java.lang.Thread.State: WAITING (on object monitor)
    	at java.lang.Object.wait(Native Method)
    	- waiting on <0x00000005c8b08e98> (a java.lang.ref.ReferenceQueue$Lock)
    	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
    	- locked <0x00000005c8b08e98> (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=0x00007f4168099800 nid=0x561f in Object.wait() [0x00007f40b0cfe000]
       java.lang.Thread.State: WAITING (on object monitor)
    	at java.lang.Object.wait(Native Method)
    	- waiting on <0x00000005c8b06b40> (a java.lang.ref.Reference$Lock)
    	at java.lang.Object.wait(Object.java:502)
    	at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
    	- locked <0x00000005c8b06b40> (a java.lang.ref.Reference$Lock)
    	at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
    
    "main" #1 prio=5 os_prio=0 tid=0x00007f4168007800 nid=0x560b runnable [0x00007f4170107000]  ---只有main线程属于Java用户线程,其他都是由虚拟机自动创建的线程
       java.lang.Thread.State: RUNNABLE                                                         
    	at com.huawei.diagnose.thread.MyTest.fun2(MyTest.java:17)                           ---当前线程调用上下文,即从哪个函数中调用到哪个函数中(从下往上看),正执行到哪个类的哪一行。
    	- locked <0x00000005c8b5dce0> (a java.lang.Object)                                  ---"main" #1 prio=5 os_prio=0 tid=0x00007f4168007800 nid=0x560b runnable [0x00007f4170107000]
    	at com.huawei.diagnose.thread.MyTest.fun1(MyTest.java:11)                           ---main线程名称,prio=5线程优先级,tid=0x00007f4168007800线程id,nid=0x560b线程对应的本地线程id号(Native Thread ID),runnable线程状态
    	- locked <0x00000005c8b5dcd0> (a java.lang.Object)                                  ---线程main已经占有了锁<0x00000005c8b5dcd0>,其中<0x00000005c8b5dcd0>	                                                                                        ---标示锁ID,这个ID是系统自动产生的,我们只需要知道每次打印的堆栈,同一个ID表示是同一个锁即可
    	at com.huawei.diagnose.thread.MyTest.main(MyTest.java:25)
    
    "VM Thread" os_prio=0 tid=0x00007f4168092000 nid=0x561e runnable 
    
    "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f416801c800 nid=0x560c runnable 
    
    "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f416801e800 nid=0x560d runnable 
    
    "GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f4168020000 nid=0x560e runnable 
    
    "GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f4168022000 nid=0x560f runnable 
    
    "GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00007f4168023800 nid=0x5610 runnable 
    
    "GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00007f4168025800 nid=0x5611 runnable 
    
    "GC task thread#6 (ParallelGC)" os_prio=0 tid=0x00007f4168027000 nid=0x5612 runnable 
    
    "GC task thread#7 (ParallelGC)" os_prio=0 tid=0x00007f4168029000 nid=0x5613 runnable 
    
    "GC task thread#8 (ParallelGC)" os_prio=0 tid=0x00007f416802a800 nid=0x5614 runnable 
    
    "GC task thread#9 (ParallelGC)" os_prio=0 tid=0x00007f416802c800 nid=0x5615 runnable 
    
    "GC task thread#10 (ParallelGC)" os_prio=0 tid=0x00007f416802e000 nid=0x5616 runnable 
    
    "GC task thread#11 (ParallelGC)" os_prio=0 tid=0x00007f4168030000 nid=0x5617 runnable 
    
    "GC task thread#12 (ParallelGC)" os_prio=0 tid=0x00007f4168031800 nid=0x5618 runnable 
    
    "GC task thread#13 (ParallelGC)" os_prio=0 tid=0x00007f4168033800 nid=0x5619 runnable 
    
    "GC task thread#14 (ParallelGC)" os_prio=0 tid=0x00007f4168035000 nid=0x561a runnable 
    
    "GC task thread#15 (ParallelGC)" os_prio=0 tid=0x00007f4168037000 nid=0x561b runnable 
    
    "GC task thread#16 (ParallelGC)" os_prio=0 tid=0x00007f4168038800 nid=0x561c runnable 
    
    "GC task thread#17 (ParallelGC)" os_prio=0 tid=0x00007f416803a800 nid=0x561d runnable 
    
    "VM Periodic Task Thread" os_prio=0 tid=0x00007f41680f8800 nid=0x562f waiting on condition
    

    其中,“线程对应的本地线程ID号”是指的“本地线程”是指该Java线程所对应的虚拟机中的本地线程。Java语言中的现象是依附于Java虚拟机中的本地线程来运行的,实际上是本地线程在执行Java线程代码。Java代码中创建一个线程,虚拟机在运行期间执行到该创建线程的字节码时,就会创建一个对应的本地线程,而这个本地线程才是真正的线程实体。

    如何把Java线程堆栈里面的线程与本地线程对应上?

    以这个main线程为例

    "main" #1 prio=5 os_prio=0 tid=0x00007f5444007800 nid=0x581c runnable [0x00007f544b1ff000]
       java.lang.Thread.State: RUNNABLE
    	at com.huawei.diagnose.thread.MyTest.fun2(MyTest.java:17)
    	- locked <0x00000005c8b5dce0> (a java.lang.Object)
    	at com.huawei.diagnose.thread.MyTest.fun1(MyTest.java:11)
    	- locked <0x00000005c8b5dcd0> (a java.lang.Object)
    	at com.huawei.diagnose.thread.MyTest.main(MyTest.java:25)
    

    nid=0x581c的十进制为22556

    (1)查看Java程序的进程号jps,比如22555
    (2)top -p 22555,进入top界面,然后按H,可以线程该Java虚拟机里面所有的线程,发现有一个22556的pid就是main所对应的本地线程

    root@ubuntu-blade2:/sdf# top -p 22555
    top - 14:57:57 up 107 days,  3:37,  3 users,  load average: 0.99, 0.60, 0.45
    Tasks:  38 total,   1 running,  37 sleeping,   0 stopped,   0 zombie
    Cpu(s):  4.4%us,  0.1%sy,  0.0%ni, 95.5%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
    Mem:  98956100k total, 54769432k used, 44186668k free,  1350856k buffers
    Swap: 100632572k total,        0k used, 100632572k free, 47744612k cached
    
       PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                                                                                                                                                        
     22556 root      20   0 28.2g  22m  10m R 99.8  0.0   0:43.70 java                                                                                                                                                                                                            
     22555 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22557 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22558 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22559 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22560 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22561 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22562 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22563 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22564 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22565 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22566 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22567 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22568 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22569 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22570 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22571 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22572 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22573 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22574 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22575 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22576 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22577 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22578 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22579 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22580 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22581 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22582 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22583 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22584 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22585 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22586 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22587 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22588 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22589 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22590 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22591 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.00 java                                                                                                                                                                                                            
     22592 root      20   0 28.2g  22m  10m S  0.0  0.0   0:00.02 java
    

    从分析可以看出,Java线程设计上和本地线程指的是同一个实体,只有本地线程才是真正的线程实体,Java线程实际上就是指这个本地线程,它并不是另外存在的实体。

    "main" #1 prio=5 os_prio=0 tid=0x00007f5444007800 nid=0x581c runnable [0x00007f544b1ff000]
    中的runnable标示当前线程处于运行状态。这个runnable状态是从虚拟机的角度来看的,标示这个线程正在运行。但是处于runnable状态的线程不一定真的消耗CPU。
    处于Runnable的线程只能说明该线程没有阻塞在Java的wait或sleep方法上,同时也没等待在锁上面。
    但是如果该线程调用了本地方法,而本地方法处于等待状态,这个时候虚拟机是不知道本地代码中发生了什么,此时尽管当前线程实际上也是阻塞的状态,但实际上显示出来的还是Runnable状态,这种情况下是不消耗CPU的。
    也就是说,Runnable状态不意味这个线程正在消耗CPU。
    堆栈信息里面包含Native Method或Compiled Code
    表示一个是本地方法JNI,一个是该class的方法已经被JIT编译成了本地代码

    2.2 锁的解读  

    wait当线程执行到wait方法上,当前线程会释放监视锁,此时其他线程可以占用该锁,一旦wait方法执行完成,当前线程会继续参与锁的竞争,直到执行完该锁的作用域。
    正是由于wait的这个特性:一旦执行到一个锁的wait方法,该线程就会释放这个锁,所以可以有多个线程一起进入到同步块中。

    sleep该方法与锁操作无关,如果该方法恰好在一个锁的保护范围内,当前线程即使在执行sleep的时候,仍然继续保持监视锁。也就说说该方法实际上是和锁操作无关的。

    锁的状态目前主要有三种:
    (1)当一个线程占有一个锁的时候,线程堆栈会打印locked <xxx>
    (2)当一个线程正在等待其他线程释放该锁,线程堆栈会打印waiting to lock <xxx>
    (3)当一个线程占有一个锁,但又执行到该锁的wait上,线程堆栈会先打印locked <xxx>,然后又会打印waiting on <xxx>

    java源代码

    public class ThreadTest {
    
        public static void main(String[] args) {
            Object share = new Object();
            TestThreadLocked thread1 = new TestThreadLocked(share);
            thread1.start();
    
            TestThreadWaitingTo thread2 = new TestThreadWaitingTo(share);
            thread2.start();
    
            TestThreadWaitingOn thread3 = new TestThreadWaitingOn();
            thread3.start();
    
    
        }
    }
    
    class TestThreadLocked extends Thread {
        Object lock = null;
        public TestThreadLocked(Object lock) {
            this.lock = lock;
            this.setName(this.getClass().getName());
        }
    
        @Override
        public void run() {
            fun();
        }
        public void fun() {
            synchronized (lock) {
             funLongTime();
            }
        }
    
        public void funLongTime() {
            try {
                Thread.sleep(20000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    class TestThreadWaitingOn extends Thread {
        Object lock = new Object();
        public TestThreadWaitingOn() {
            this.setName(this.getClass().getName());
        }
    
        @Override
        public void run() {
            fun();
        }
    
        public void fun() {
            synchronized (lock) {
             funWait();
            }
        }
    
        public void funWait() {
            try {
                lock.wait(100000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    class TestThreadWaitingTo extends Thread {
        Object lock = null;
        public TestThreadWaitingTo(Object lock) {
            this.lock = lock;
            this.setName(this.getClass().getName());
        }
    
        @Override
        public void run() {
            fun();
        }
    
        public void fun() {
            synchronized (lock) {
                funLongTime();
            }
        }
    
        public void funLongTime() {
            try {
                Thread.sleep(20000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    
    }

    线程堆栈如下:

    "com.huawei.diagnose.thread.TestThreadWaitingOn" #20 prio=5 os_prio=0 tid=0x00007f2a1010e000 nid=0x5cb7 in Object.wait() [0x00007f29cf2f1000]
       java.lang.Thread.State: WAITING (on object monitor)
    	at java.lang.Object.wait(Native Method)
    	- waiting on <0x00000005c8b630c0> (a java.lang.Object) --执行到锁0x00000005c8b630c0的wait方法
    	at java.lang.Object.wait(Object.java:502)
    	at com.huawei.diagnose.thread.TestThreadWaitingOn.funWait(ThreadTest.java:66)
    	at com.huawei.diagnose.thread.TestThreadWaitingOn.fun(ThreadTest.java:60)
    	- locked <0x00000005c8b630c0> (a java.lang.Object) --已经占有了锁0x00000005c8b630c0
    	at com.huawei.diagnose.thread.TestThreadWaitingOn.run(ThreadTest.java:55)
    
    "com.huawei.diagnose.thread.TestThreadWaitingTo" #19 prio=5 os_prio=0 tid=0x00007f2a1010c800 nid=0x5cb6 waiting for monitor entry [0x00007f29cf3f2000]
       java.lang.Thread.State: BLOCKED (on object monitor)
    	at com.huawei.diagnose.thread.TestThreadWaitingTo.fun(ThreadTest.java:87)
    	- waiting to lock <0x00000005c8b5dbf8> (a java.lang.Object) --锁0x00000005c8b5dbf8已经被TestThreadLocked占有,TestThreadWaitingTo只能等待
    	at com.huawei.diagnose.thread.TestThreadWaitingTo.run(ThreadTest.java:82)
    
    "com.huawei.diagnose.thread.TestThreadLocked" #18 prio=5 os_prio=0 tid=0x00007f2a1010a800 nid=0x5cb5 waiting on condition [0x00007f29cf4f3000]
       java.lang.Thread.State: TIMED_WAITING (sleeping)
    	at java.lang.Thread.sleep(Native Method)
    	at com.huawei.diagnose.thread.TestThreadLocked.funLongTime(ThreadTest.java:40)
    	at com.huawei.diagnose.thread.TestThreadLocked.fun(ThreadTest.java:34)
    	- locked <0x00000005c8b5dbf8> (a java.lang.Object) --该线程占有锁0x00000005c8b5dbf8
    	at com.huawei.diagnose.thread.TestThreadLocked.run(ThreadTest.java:30)
    

    2.3 线程状态的解读  

    Java的线程状态有如下几类:
    RUNNABLE从虚拟机的角度看,线程处于正在运行状态。处于Runnable的线程不一定消耗CPU。
    比如线程正在从网络读取数据,尽管线程显示weightRunnable状态,但实际上网络IO,线程绝大多数时间是被挂起,只有当数据到达之后,线程才被重新唤醒,挂起发生在本地代码中,虚拟机根本不知道,像这种socket IO操作,不会消耗大量的CPU,因为大多时间在等待,只有数据到来之后,才消耗一点CPU。

    TIME_WAITING(on object monitor)表示当前线程被挂起一段时间,说明该线程正在执行obj.wait(int time)方法。

    TIMED_WAITING(sleeping)表示当前线程被挂起一段时间,即正在执行Thread.sleep(int time)方法。

    TIMED_WAITING(parking)当前线程被挂起一段时时间,即正在执行LockSupport.parkNanos()。

    WAITING(on object monitor)当前线程被挂起,即正在执行obj.wait()方法,无参数的wait方法

    总结:处于TIMED_WAITING,WAITING状态的线程一定不消耗CPU,处于RUNNABLE的线程,要结合当前线程代码的性质判断,是否消耗CPU
    (1)如果是纯Java运算代码,则消耗CPU
    (2)如果是网络IO,很少消耗CPU
    (3)如果是本地代码,结合本地代码的性质判断,如果是纯运算代码,则消耗CPU,如果被挂起,则不消耗CPU,如果是IO,则不怎么消耗CPU。

    3. 借助线程堆栈进行问题分析  

    看堆栈一般从三个视角来分析:堆栈的局部信息,一次堆栈的统计信息(全局信息),多个堆栈的对比信息(打印一次堆栈是一个切面,打印多次堆栈就是立体了)。

    3.1 线程死锁分析  

    Java源代码

    public class TestDeadLock {
    
        public static void main(String[] args) {
            Object lock1 = new Object();
            Object lock2 = new Object();
            TestThread1 thread1 = new TestThread1(lock1, lock2);
            thread1.start();
    
            TestThread2 thread2 = new TestThread2(lock1, lock2);
            thread2.start();
        }
    
        static class TestThread1 extends Thread {
            Object lock1 = null;
    
            Object lock2 = null;
    
            public TestThread1(Object lock1, Object lock2) {
                this.lock1 = lock1;
                this.lock2 = lock2;
                this.setName(this.getClass().getName());
            }
    
            @Override
            public void run() {
                fun();
            }
    
            public void fun() {
                synchronized (lock1) {
                    try {
                        Thread.sleep(2);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    new Throwable().printStackTrace();
                    synchronized (lock2) {
                    }
                }
            }
        }
    
        static class TestThread2 extends Thread {
            Object lock1 = null;
    
            Object lock2 = null;
    
            public TestThread2(Object lock1, Object lock2) {
                this.lock1 = lock1;
                this.lock2 = lock2;
                this.setName(this.getClass().getName());
            }
    
            @Override
            public void run() {
                fun();
            }
    
            public void fun() {
                synchronized (lock2) {
                    try {
                        Thread.sleep(2);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    synchronized (lock1) {
                    }
    
                }
            }
        }
    
    }
    

    堆栈如下:

    Found one Java-level deadlock:
    =============================
    "com.huawei.diagnose.thread.TestDeadLock$TestThread2":
      waiting to lock monitor 0x00007f9db0004e28 (object 0x00000005c8b5dc80, a java.lang.Object),
      which is held by "com.huawei.diagnose.thread.TestDeadLock$TestThread1"
    "com.huawei.diagnose.thread.TestDeadLock$TestThread1":
      waiting to lock monitor 0x00007f9db00062c8 (object 0x00000005c8b5dc90, a java.lang.Object),
      which is held by "com.huawei.diagnose.thread.TestDeadLock$TestThread2"
    
    Java stack information for the threads listed above:
    ===================================================
    "com.huawei.diagnose.thread.TestDeadLock$TestThread2":
    	at com.huawei.diagnose.thread.TestDeadLock$TestThread2.fun(TestDeadLock.java:70)
    	- waiting to lock <0x00000005c8b5dc80> (a java.lang.Object)
    	- locked <0x00000005c8b5dc90> (a java.lang.Object)
    	at com.huawei.diagnose.thread.TestDeadLock$TestThread2.run(TestDeadLock.java:59)
    "com.huawei.diagnose.thread.TestDeadLock$TestThread1":
    	at com.huawei.diagnose.thread.TestDeadLock$TestThread1.fun(TestDeadLock.java:41)
    	- waiting to lock <0x00000005c8b5dc90> (a java.lang.Object)
    	- locked <0x00000005c8b5dc80> (a java.lang.Object)
    	at com.huawei.diagnose.thread.TestDeadLock$TestThread1.run(TestDeadLock.java:29)
    
    Found 1 deadlock.
    

    从打印的线程堆栈中能看到Found one Java-level deadlock,即如果存在线程死锁情况,虚拟机在输出的堆栈中会直接给出死锁的分析结果。
    死锁的两个或多个线程是不消耗CPU的,鱼人认为CPU100%的使用率是线程死锁导致的,这个说法是完全错误的,无限循环(死循环),并且在循环中代码都是CPU密集型,才有可能导致CPU的100%使用率,像socket或数据库等IO操作消耗的CPU非常小。

    3.2 Java代码死循环等导致的CPU过高分析  

    具体步骤如下:
    (1)获取第一次堆栈信息
    (2)等待一定的时间,获取第二次杜子涵信息
    (3)预处理两次堆栈信息,去掉处于sleeping或waiting状态的线程,因为这种线程是不消耗CPU的
    (4)比较第一次堆栈和第二次堆栈预处理的线程,找出这段时间一直活跃的线程,如果两次堆栈张同一个线程处于同样的调用上下文,那么久应该列为重点怀疑对象。
    如果通过堆栈定位,没有发现热点代码段,那么CPU过高可能是不恰当的内存设置导致的频繁GC,从而导致CPU过高。
    如果系统进入死循环,假设死循环中的代码都是CPU密集型的,那么在单核的机器上CPU的使用率应该为100%,在多核机器上应该有一个CPU核的使用率100%。

    3.3 高消耗CPU代码的常见分析方法

    如下原因都可能造成CPU使用率异常:
    (1)Java代码中存在死循环导致CPU过高
    (2)系统存在不恰当的设计,尽管没有死循环,但仍然CPU过高,比如无间断的轮循
    (3)JNI中有死循环代码
    (4)对内存设置太小造成的频繁GC(堆太小或内存泄漏)
    (5)32位JDK下,对内存设置太大造成的频繁GC
    (6)JDK自身存在死循环的Bug

    3.4 资源不足等导致的性能下降分析

    这里所说的资源包括数据库连接等。大多时候资源不足和性能瓶颈是同一类问题。当资源不足,就会导致资源争用,请求该资源的线程会被阻塞或挂起,自然就导致性能下降。
    对于资源不足的导致的性能瓶颈,打印出的线程堆栈有如下特点:大量的线程停在同样的调用上下文。
    导致资源不足的原因可能:(1)资源数配置太少(2)获得资源的线程把持资源时间太久,导致资源不足(3)资源用完后,在某种情况下,没有关闭或回池,导致可用资源泄露或减少。

    3.5 线程不退出导致的系统挂死分析

    导致系统挂死的原因有很多,其中有一个最常见的原因是线程挂死。
    具体导致线程无法退出的原因有很多:
    (1)线程正在执行死循环
    (2)资源不足或资源泄露,造成当前线程阻塞在锁对象上(即wait在锁对象上)长期得不到唤醒notify
    (3)如果当前程序和外部通信,当外部程序挂起无返回时,也会导致当前线程挂起。

    3.6 多个锁导致的锁链分析

    3.7 线程堆栈不能分析什么问题?

    线程堆栈定位问题,只能定位子在当前线程上留下痕迹的问题,但线程堆栈对于不留痕迹的问题,就无能为力。

    4. 通过Java线程堆栈进行性能瓶颈分析

    性能提高,需要且仅需要解决当前的受限资源,当前受限资源可能是:
    (1)CPU,如果当前CPU已经能够接近100%的利用率,并且代码业务逻辑无法再简化,那么说明该系统已经达到了性能最大化,如果再想提高性能,只能增加处理器。
    (2)其他资源,如数据库连接数量等,如果CPU利用率总是无法接近100%,那么通过修改代码尽量提高CPU的使用率,那么整体性能也会获得极大提高。
    性能调优的终极目标是:逐渐增大压力,系统的CPU利用率能够接近100%。
    如果系统具有如下特点,说明这个系统存在性能瓶颈:
    (1)随着系统逐步增加压力,但是CPU的使用率无法趋近于100%,尽管还有可用的CPU,但是压力楞是压不上
    (2)持续运行缓慢,时常发现应用程序运行缓慢,通过改变环境因子如负载量、数据库连接等,也无法有效提升整体响应时间
    (3)系统性能随时间的增加逐渐下降,在负载稳定的情况下,系统运行时间越长速度越慢

    4.1 常见的性能瓶颈

    4.1.1 由于不恰当的同步导致的资源争用

    (1)不相关的两个函数,共用了一个锁,或不同的共享变量共用了同一个锁,无谓地制造出了资源争用。
    只有访问共享变量的代码段才需要使用锁保护,而且每一个共享变量对应一个自己的锁,而不要让所有的共享变量使用同一把锁。同时,如果可能尽量避免将synchronized加在整个方法上面,而是将synchronized加在尽量少的代码上。
    (2)锁的粒度过大

    4.1.2 sleep的滥用

    这种设计肯定可以使用notify和wait来完成同样的功能。

    4.1.3 String +的滥用

    每次+操作都会产生一个临时对象,并伴随着数据拷贝,这个对性能是一个极大的消耗。

    4.1.4 不恰当的线程模型

    在多线程场景下,如果线程模型不恰当,也会使性能低下。

    4.1.5 效率低下的SQL语句或不恰当的数据库设计

    4.1.6 不起当的GC参数设置导致的性能低下

    4.1.7 线程数量不足

    4.1.8 内存泄漏导致的频繁GC

    内存泄漏会导致GC越来越频繁,而GC操作死CPU密集型操作,频繁GC会导致系统整体性能严重下降。

    4.2 性能分析调优的终结条件

    总的原则是在系统中算法已经足够简化,即从算法的角度无法提升性能时,当增加压力时,CPU上升,随着压力的增加,CPU的使用率能趋向于100%,此时说明系统的性能已经榨干,即系统满足如下两个条件,那么性能调优即结束:
    (1)算法足够优化
    (2)没有线程/资源的使用不当而导致的CPU利用不足

  • 相关阅读:
    HTML基础之HTML标签-html header(meta,title) html body(p,br,h,form,div,span,input,lable)
    day5_非空即真非零即真
    day5_不能循环删除list-深拷贝、浅拷贝(import copy)
    day5_函数返回值
    day4_局部变量和全局变量
    day5_函数的参数
    python字符串与列表的相互转换
    python自动化返回:no tests were fount Empty test suite
    读取Excel数据封装
    测试工程题
  • 原文地址:https://www.cnblogs.com/lujiango/p/10952026.html
Copyright © 2011-2022 走看看