JDK中除了提供大量的命令行之外,還提供兩個功能強大的可視化工具:JConsole和VisualVM。 之前對java的調試一直停留在 右鍵->debug as ,實在慚愧,今天看了下,索性當個讀書筆記記錄,果然我JAVA大法好,hhh......
通過D:\Program Files\Java\jdk1.7.0_71\bin
找到jconsole.exe
啟動,這個位置取決於本機安裝的java路徑。類似jps命令,顯示本機運行的所有虛擬機進程。可支持本地進程和遠程進程。
雙擊選擇一個進程,看到如下:
“概述”顯示整個虛擬機主要運行數據的概覽,其中包括“堆內存使用情況”、“線程”、“類”、“CPU使用情況”4中信息的曲線圖,這些曲線圖是後面“內存”、“線程”、“類”頁簽的信息匯總。
相當於可視化的jstat
命令,用於監視受收集器管理的虛擬機內存(JAVA堆和永久代)的變化趨勢。
相當於可視化jstack
命令,遇到線程停頓時可是用這個頁簽進行監控分析。而jstack命令中 線程長時間停頓的主要原因有:
等待外部資源(數據庫連接,網絡資源,設備資源等)
死循環
鎖等待(活鎖和死鎖)
通過例子進行說明:
代碼如下:
package com.xjtu.imiss.chapter4;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class MonitoringTest {
/**
* 線程死循環演示
*/
public static void createBusyThread() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true);// 第15行
}
}, "testBusyThread");
thread.start();
}
/**
* 線程鎖等待演示
*/
public static void createLockThread(final Object lock) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "testLockThread");
thread.start();
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine();
createBusyThread();
br.readLine();
Object obj = new Object();
createLockThread(obj);
}
}
程序運行後,首先在“線程”頁簽選擇main線程,如下所示:
堆棧追蹤顯示BufferedReader
在readBytes方法中等待System.in的鍵盤輸入,這時線程為Runnable狀態,Runnable狀態的線程會被分配運行時間,但readBytes方法檢查到流沒有更新時候就會歸還執行令牌,這種等待只消耗很小的CPU資源。
下來看testBusyThread線程,一直執行空循環,從狀態中看到代碼停留在while(true)這一行,此時線程為Runnable裝啊提,而且沒有歸還線程令牌的動作會在空循環用盡全部執行時間,直到線程切換,這種等待比較消耗CPU資源。
下來是testLockThread線程等到這lock對象的notify 或者notifyAll方法的出現,此時線程處於waiting狀態,在被喚醒之前不會被分配執行時間。
testLockThread處於正常的活鎖等待,只要lock對象的notify()或者notifyAll()方法被調用,這個線程就能激活並繼續執行。下面演示一個無法再被激活的死鎖等待。
package com.xjtu.imiss.chapter4;
public class MonitoringTest {
/**
* 線程死鎖等待演示
*/
static class SynAddRunalbe implements Runnable {
int a, b;
public SynAddRunalbe(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public void run() {
synchronized (Integer.valueOf(a)) {
synchronized (Integer.valueOf(b)) {
System.out.println(a + b);
}
}
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new SynAddRunalbe(1, 2)).start();
new Thread(new SynAddRunalbe(2, 1)).start();
}
}
}
這段代碼開了200個線程分別計算1+2以及2+1的值,其實for循環可省略,因為兩個線程也能導致死鎖,不過那樣概率很小,需要嘗試運行多詞才能看到效果。一般的話,帶for循環的版本最多運行2~3次就會遇到線程死鎖,程序無法結束。造成死鎖的原因是Integer.valueOf()方法基於減少對象創建和節省內存的考慮,[-128,127]之間的數字會被緩存,當valueOf()方法傳入參數在這個范圍內,將直接返回緩存中的對象。也就是說,代碼調用了200次Integer.valueOf()一共就返回兩個不同的對象。
假如在某個線程的synchronized塊之間發生了一次線程切換,那就會出現線程A等待線程B持有的Integer.valueOf(1),線程B又等待線程A持有的Integer.valueOf(2),結果大家都跑不下去了。
點擊“檢測到死鎖”,查看結果如下:
主要參考深入理解JAVA虛擬機 jvm高級特性和最佳實踐這本書,感謝作者~多謝大牛~~~