Jconsole: JAVA 監視和管理控制台簡介


###Jconsole: JAVA 監視和管理控制台簡介 JDK中除了提供大量的命令行之外,還提供兩個功能強大的可視化工具:JConsole和VisualVM。 之前對java的調試一直停留在 右鍵->debug as ,實在慚愧,今天看了下,索性當個讀書筆記記錄,果然我JAVA大法好,hhh......

啟動JConsole

通過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高級特性和最佳實踐這本書,感謝作者~多謝大牛~~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM