JConsole: Java監視與管理控制台
代碼清單1:
import java.util.*; public class JConsoleDemo { static class OOMObject { public byte[] placeholder = new byte[64 * 1024]; } public static void fillHeap(int num) throws InterruptedException { List<OOMObject> list = new ArrayList<OOMObject>(); for (int i = 0; i < num; i++) { Thread.sleep(50); list.add(new OOMObject()); } System.gc(); } public static void main(String[] args) throws Exception { fillHeap(1000); //System.gc(); Thread.sleep(10000); } }
內存監控:
編譯運行JConsoleDemo類, 運行時設置的虛擬機參數為 -Xms100m -Xmx100m -XX:+UseSerialGC , 在%JAVA_HOME%\bin目錄下, 啟動jconsole.exe , 將自動搜索出本機運行的所有虛擬機進程, 這里我們選擇JConsoleDemo對應的進程2464。
啟動后主界面如下:
在"內存"頁簽, 查看堆內存Eden區的運行趨勢如下:
從圖中詳細信息可以看出, Eden區的內存大小為27.328KB, 所以折線圖中顯示每次到27Mb左右時系統就會進行一次GC。當1000次循環結束后, 執行System.gc(), 柱狀圖中顯示Eden區和Survivor區基本被清空, 但老年代的對應柱狀圖仍保持峰值狀態, 這是因為System.gc()是在fillHeap()方法內執行, 所以list對象在System.gc()執行時仍然是存活的( 處於作用域之內、被引用)。如果將System.gc()移動到fillHeap()方法外執行, 如下柱狀圖所示, 則會回收包括老年代的所有內存。
代碼清單2:
import java.io.*; public class JConsoleDemo2 { 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) { new Thread(new SynAddRunalbe(1, 2)).start(); new Thread(new SynAddRunalbe(2, 1)).start(); } }
線程監控:
編譯運行, 在"線程"頁簽可查看"死鎖"描述。這是因為1、2兩個數值在Integer類的緩存常量池[-128, 127]范圍內, 這樣當多次調用Integer.valueOf()方法時, 不會再每次都創建對象, 而是直接返回緩存常量池中的對象。所以上面兩個線程的同步代碼塊中實際上只創建了兩個鎖對象, 且在某一時刻互相持有對方的鎖, 即"死鎖"現象。
VisualVM: 多合一故障處理工具
概述與插件安裝
VisualVM基於NetBeans平台開發, 因此它一開始就具備了插件擴展的特性, 通過插件支持, VisualVM可以做許多事情, 例如:
- 顯示虛擬機進程和進程的配置、環境信息(jps、jinfo)
- 監視應用程序的CPU、GC、堆、方法區及線程的信息(jstat、jstack)
- dump及分析堆轉儲快照(jmap、jhat)
- 方法級的程序運行性能分析, 找出被調用最多、運行時間最長的方法
- 離線程序快照: 收集程序的運行時配置、線程dump、內存dump等信息建立一個快照, 可以將快照發送開發者處進行bug反饋等等
在%JAVA_HOME%\bin目錄下, 啟動jvisualvm.exe進入主界面, 點擊"工具"→"插件"→"可用插件"選項, 選擇所需的插件安裝。
安裝好插件后, 選擇一個正在運行的java程序就可以查看程序監控的主界面了
堆轉儲快照
兩種方式生成堆dump文件:
- 在"應用程序"窗口中右鍵單擊應用程序節點, 選擇"堆 Dump"
- 在"監視"頁簽中選擇"堆 Dump"
分析程序性能
在Profiler頁簽中, 可以對程序運行期間方法級的CPU和內存進行分析, 這個操作會對程序運行性能有很大影響, 所以一般不再生產環境使用。CPU分析將會統計每個方法的執行次數、執行耗時; 內存分析則會統計每個方法關聯的對象數及對象所占空間。
BTrace動態日志跟蹤
BTrace既可以作為visualVM的插件來使用, 也可以獨立運行。它能在不停止目標程序運行的前提下, 通過熱部署技術加入原本並不存在的調試代碼, 以實現對程序的動態調試。
上面我們已經安裝好了BTrace插件, 這里編譯運行BTraceTest。
import java.io.*; public class BTraceTest { public int add(int a, int b) { return a + b; } public static void main(String[] args) throws IOException { BTraceTest test = new BTraceTest(); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); for (int i = 0; i < 10; i++) { reader.readLine(); int a = (int) Math.round(Math.random() * 1000); int b = (int) Math.round(Math.random() * 1000); System.out.println(test.add(a, b)); } } }
在"應用程序"頁簽中選擇對應的java進程, 右鍵選擇"Trace Application", 進入BTrace面板:
在BTrace頁簽內輸入調試代碼:
/* BTrace Script Template */ import com.sun.btrace.annotations.*; import static com.sun.btrace.BTraceUtils.*; @BTrace public class TracingScript { @OnMethod( clazz="BTraceTest", method="add", location=@Location(Kind.RETURN) ) public static void func(@Self BTraceTest instance,int a,int b,@Return int result) { println("調用堆棧:"); jstack(); println(strcat("方法參數A:",str(a))); println(strcat("方法參數B:",str(b))); println(strcat("方法結果:",str(result))); } }
點擊BTrace面板的"Start"按鈕, 對程序進行動態調試:
BTrace不僅可以用來打印調用堆棧、參數、返回值, 還可以進行性能監控、定位連接泄漏和內存泄漏、解決多線程競爭問題, 這里我們只是做一個入門的了解, 具體的拓展應用以后會專門用一篇文章來講解。
參考資料
作者:張小凡
出處:https://www.cnblogs.com/qingshanli/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。如果覺得還有幫助的話,可以點一下右下角的【推薦】。