註意:該篇博客主要記錄自《深入理解java虛擬機(第二版)》 說明:關於命令行的JVM性能監控與故障處理工具見《第七章 JVM性能監控與故障處理工具(1)》 1、圖像化的故障處理工具 Jconsole visualVM 2、Jconsole 進入"E:\Java\jdk1.6\bin",雙擊"jco
註意:該篇博客主要記錄自《深入理解java虛擬機(第二版)》
說明:關於命令行的JVM性能監控與故障處理工具見《第七章 JVM性能監控與故障處理工具(1)》
1、圖像化的故障處理工具
- Jconsole
- visualVM
2、Jconsole
進入"E:\Java\jdk1.6\bin",雙擊"jconsole.exe",彈出如下框:
說明:這裡列出了所有的JVM進程,一個Jconsole進程,一個eclipse(PID:4684),這相當於jps命令。
選中其中一個PID,假設選中了eclipse,雙擊,出現下圖:(註:之後的各個葉簽,都是每4秒刷新一次)
"記憶體":相當於jstat -gc,在上圖中的詳細信息部分,該部分對應的信息就是頭部圖表部分所寫的參數(這裡是"整個堆"的情況),同時對應的也是右下角部分柱狀圖所選中的柱子(這裡是"堆"),對於"非堆"指的就是"方法區"(或者稱為"永久代")。當然,這裡也可以選擇時間範圍來查看相應的信息。
"類":相當於jstat -class,列出了裝載類和卸載類的相關信息。
"線程":相當於jstack,折線圖顯示了線程數目的變化情況,包括峰值線程數量、活動線程數量;左下角展示了所有線程名稱。雙擊相應的線程名稱
"VM摘要":相當於jinfo
最後,測試一下線程死鎖的現象。代碼如下:
1 package thread; 2 3 /** 4 * 測試線程 5 */ 6 class XXthread implements Runnable{ 7 int a,b; 8 9 public XXthread(int a, int b) { 10 this.a = a; 11 this.b = b; 12 } 13 14 public void run() { 15 synchronized (Integer.valueOf(a)) { 16 synchronized (Integer.valueOf(b)) { 17 System.out.println(a + b); 18 } 19 } 20 } 21 } 22 23 public class TestDeadLockThread { 24 public static void main(String[] args) { 25 for(int i=0;i<100;i++){ 26 new Thread(new XXthread(1, 2)).start(); 27 new Thread(new XXthread(2, 1)).start(); 28 } 29 } 30 }View Code
執行main()方法,之後去查看"線程"標簽,點擊"檢測死鎖",如下:
發現線程Thread-95和Thread-106死鎖(彼此擁有對方想要的鎖)
分析:
1)Integer緩存機制
Integer.valueOf(int xxx),該方法為了減少對象的創建,節省記憶體,會將xxx轉化成的Integer對象緩存起來,之後只要是相同的xxx,那麼這個方法都會直接從緩存中取出對象來。假設代碼中的Integer.valueOf(1)生成的對象是java.lang.Integer@987197,而Integer.valueOf(2)生成的對象是java.lang.Integer@15e293a,那麼之後無論調用多少次Integer.valueOf(1),也無論是哪一個線程去調用該方法,返回的都只是同一個對象java.lang.Integer@987197。也就是說上邊的這段代碼中的Integer.valueOf(i)只會生成兩個不同的對象,就是java.lang.Integer@987197和java.lang.Integer@15e293a,而這兩個對象也就是我們的鎖對象。
2)死鎖發生的時機
假設線程"Thread-95"執行到其第一個synchronized塊中時(假設剛剛獲取了鎖對象java.lang.Integer@987197),這時候CPU時間片切換給了線程"Thread-106",而"Thread-106"執行其第一個synchronized塊(獲取了鎖對象java.lang.Integer@15e293a),之後"Thread-106"要執行第二個synchronized塊兒來獲取鎖對象java.lang.Integer@987197,這時候就獲取不到了,因為這個鎖對象正被"Thread-95"所持有,於是"Thread-106"就阻塞在java.lang.Integer@987197這個鎖對象上,這時,假設CPU時間片又切換給了"Thread-95",該線程要執行第二個synchronized塊來獲取java.lang.Integer@15e293a,就獲取不到了,因為該鎖對象已被"Thread-106"所持有
3)結果
"Thread-95"持有鎖對象java.lang.Integer@987197,阻塞在鎖對象java.lang.Integer@15e293a;
"Thread-106"持有鎖對象java.lang.Integer@15e293a,阻塞在鎖對象java.lang.Integer@987197
3、visualVM
是一塊更加全面的GUI監視工具,包含很多插件(需要自己下載),具體的見《深入理解Java虛擬機(第二版)》