以前原本打算結合本身寫的小程序來介紹JConsole和VisualVM的使用的,可是發現很難經過一個程序把全部的場景都體現出來,因此仍是決定用書中的典型小例子來說更加清晰。 java
JConsole是一個機遇JMX(Java Management Extensions,即Java管理擴展)的JVM監控與管理工具,監控主要體如今:堆棧內存、線程、CPU、類、VM信息這幾個方面,而管理主要是對JMX MBean(managed beans,被管理的beans,是一系列資源,包含對象、接口、設備等)的管理,不只能查看bean的屬性和方法信息,還可以在運行時修改屬性或調用方法。
首先咱們看下JConsole的啓動,JConsole在jdk/bin/下,其啓動須要圖形界面的支持(廢話,都說了圖形界面),可能很多人一聽到這個就以爲有點low:平時服務器跑的linux都沒圖形界面,那豈不是用不了。其實不用擔憂,JConsole支持遠程進程監測。下邊是鏈接界面,其實至關於jps命令:
linux
再來看下鏈接後的界面,咱們打開DeadLock(一個測試死鎖的示例): 算法
能夠看到上邊的選項卡正好對應各個功能。
小程序
下邊來看兩個小示例,分別分析內存和死鎖的。 緩存
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進程,當程序結束時堆內存以下:
能夠看到對內存峯值在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,咱們來看下運行時內存圖:
會發現堆內存呈規律的折線,咱們來分析下:當每一個對象實例化後,而後置null,這時候對象並不會被回收(由於沒有gc),所以內存會一直上升,可是當堆內存不夠用時,會觸發gc,所以內存會下降。查看VM概況可知,一共進行了18次gc,回收算法爲「複製」。 多線程
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源碼的時候見過不少這種處理方法)。 好了,來看下如何檢查死鎖。
以上就是JConsole的基本用法,仍是比較簡單的。但這些只是小道,工具畢竟只是輔助,關鍵的仍是要懂得原理,學會分析,真正的能在實踐中活用纔好。下次將介紹更爲強大的VisualVM,敬請期待嘍^_^。 ide