日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

Java线程堆栈分析

發布時間:2024/8/24 综合教程 29 生活家
生活随笔 收集整理的這篇文章主要介紹了 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利用不足

總結

以上是生活随笔為你收集整理的Java线程堆栈分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。