程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA編程入門知識 >> Jconsole: JAVA 監視和管理控制台簡介

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

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved