JVM學習總結五(番外)——JConsole


    之前本來打算結合自己寫的小程序來介紹JConsole和VirtualVM的使用的,但是發現很難通過一個程序把所有的場景都體現出來,所以還是決定用書中的典型小例子來講更加清晰。

一、JConsole的基本功能

    JConsole是一個機遇JMX(Java Management Extensions,即Java管理擴展)的JVM監控與管理工具,監控主要體現在:堆棧內存、線程、CPU、類、VM信息這幾個方面,而管理主要是對JMX MBean(managed beans,被管理的beans,是一系列資源,包含對象、接口、設備等)的管理,不僅能查看bean的屬性和方法信息,還能夠在運行時修改屬性或調用方法。
    首先我們看下JConsole的啟動,JConsole在jdk/bin/下,其啟動需要圖形界面的支持(廢話,都說了圖形界面),可能不少人一聽到這個就覺得有點low:平時服務器跑的linux都沒圖形界面,那豈不是用不了。其實不用擔心,JConsole支持遠程進程監測。下邊是連接界面,其實相當於jps命令:

    再來看下連接后的界面,我們打開DeadLock(一個測試死鎖的示例):

可以看到上邊的選項卡正好對應各個功能。

1、概述

    這個不介紹了,就是上圖,相信大家都看的懂。

2、內存

    在內存頁我們可以看到程序運行期間JVM各個部分的內存狀況,右下角是對應各個分區的內存使用柱狀圖,點擊對應柱可查看詳情,看圖:

3、線程

    該頁面可以查看當前JVM進程啟動了多少個線程,並能查看每個線程的狀態及堆棧信息,此外還有一個功能就是能夠自動檢測死鎖,見圖:     

4、類

    該頁面其實和線程頁有些相似,不過顯示的是JVM加載類的信息,見圖:

5、VM概述

 

    這個其實沒必要細說,看圖就明白,顯示了當前JVM的各方面信息:



6、MBean管理

    這一部分也不細說,主要目前自己對JMX MBean不太熟悉,想要深究的就自己研究吧:

     下邊來看兩個小示例,分別分析內存和死鎖的。

二、兩個示例

1、內存分析

    這里我們來通過一個小程序進行一下內存分析,代碼如下:

package com.gj.jconsole;

import java.util.ArrayList;
import java.util.List;

public class DataInsert {

    //一個OOMObject實例大概64k+
    static class OOMObject{
        public byte[] placeholder= new byte[64*1024]; 
    }
    
    public static void fillHeap() throws InterruptedException {
        List<OOMObject> list =new ArrayList<OOMObject>();
        for(int i=0;i<1000;i++){ 
                        Thread.sleep(100);
            list.add(new OOMObject());
        }
        System.gc();
    }

    public static void main(String[] args) throws Exception{
        fillHeap();
    }
}

    可以看到程序向list中插入了1000個OOMObject對象,每個OOMObject大概64k,那么堆內存的峰值應該在64k*1000=64m左右,我們運行程序,並使用JConsole打開DataInsert進程,當程序結束時堆內存如下:

aa

    可以看到對內存峰值在60-70m之間(下方已用內存為63631kb,大約63m),與我們預計的相符。下邊我們來看下下邊這段代碼:

package com.gj.jconsole;

import java.util.ArrayList;
import java.util.List;

import com.gj.jconsole.DataInsert.OOMObject;

public class GCTest {

    // 一個OOMObject實例大概640k+
    static class OOMObject {
        public byte[] placeholder = new byte[64 * 1024*10];
    }
    
    public static void fillHeap() throws InterruptedException {
        for(int i=0;i<100;i++){
            OOMObject oOmObject =new OOMObject();
            Thread.sleep(1000);
            oOmObject=null;
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        fillHeap();
    }

}

    這段代碼每次新建一個OOMObject對象,在暫停1s后將其置null,我們來看下運行時內存圖:
aa

    會發現堆內存呈規律的折線,我們來分析下:當每個對象實例化后,然后置null,這時候對象並不會被回收(因為沒有gc),因此內存會一直上升,但是當堆內存不夠用時,會觸發gc,因此內存會降低。查看VM概況可知,一共進行了18次gc,回收算法為“復制”。

2、線程死鎖

    通過Jconsole不僅可以查看線程信息,而且能夠檢測死鎖,先來看下代碼:

package com.gj.jconsole;

public class DeadLock {

    static class SynAddRunable implements Runnable{
        int a,b;
        public SynAddRunable(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);
                }
            }
        }
    }
    
    /**
     * @param args
     */
    public static void main(String[] args) {
        for(int i=0;i<100;i++){
            new Thread(new SynAddRunable(1, 2)).start();
            new Thread(new SynAddRunable(2, 1)).start();
        }
    }
}

    可以看到代碼中啟動200個子線程,進行1+2或2+1的計算,但是這種情況為什么會出現死鎖呢?我們看到在run中出現雙重sychronized,這是典型的死鎖特征,但是這種情況要出現死鎖前提是多線程中sychronized同步的兩個對象分別都是同一個,才會造成互鎖,但是Integer.valueOf(a)和Integer.valueOf(b)每次返回的不都是一個新對象嗎?這里需要注意一個問題,為了節省內存,對於[-128,127]以內的轉換,Integer.valueOf會將這些值從緩存直接返回,所以相同的值返回的都是同一個對象(記得看java源碼的時候見過很多這種處理方法)。好了,來看下如何檢查死鎖。
    等程序運行一段時間之后(這種情況下形成死鎖是隨機的,並不能確定那兩個會互鎖,但是對於200個線程概率還是非常大的),我們在線程頁點擊“檢測死鎖”,則會多出來一個死鎖頁,打開可以看到如下信息:

aa

aa

    可以看到線程0和線程11互鎖,同時線程199由於等待線程11釋放鎖,也被阻塞。

    以上就是JConsole的基本用法,還是比較簡單的。但這些只是小道,工具畢竟只是輔助,關鍵的還是要懂得原理,學會分析,真正的能在實踐中活用才好。下次將介紹更為強大的VirtualVM,敬請期待嘍^_^。


免責聲明!

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



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