java監控工具VisualVM
java監控工具VisualVM
https://visualvm.github.io/
https://visualvm.github.io/documentation.html
https://htmlpreview.github.io/?https://raw.githubusercontent.com/visualvm/visualvm.java.net.backup/master/www/zh_CN/gettingstarted.html
https://htmlpreview.github.io/?https://raw.githubusercontent.com/visualvm/visualvm.java.net.backup/master/www/zh_CN/intro.html
轉自:http://www.cnblogs.com/wade-xu/p/4369094.html
這篇總結的很不錯(本人親自操手學習),留著以後複習備用,很適合入門級的學習者:
VisualVM 是一款免費的,集成了多個 JDK 命令列工具的視覺化工具,它能為您提供強大的分析能力,對 Java 應用程式做效能分析和調優。這些功能包括生成和分析海量資料、跟蹤記憶體洩漏、監控垃圾回收器、執行記憶體和 CPU 分析,同時它還支援在 MBeans 上進行瀏覽和操作。本文主要介紹如何使用 VisualVM 進行效能分析及調優。
準備工作
自從 JDK 6 Update 7 以後已經作為 Oracle JDK 的一部分,位於 JDK 根目錄的 bin 資料夾下,無需安裝,直接執行即可。
記憶體分析篇
VisualVM 通過檢測 JVM 中載入的類和物件資訊等幫助我們分析記憶體使用情況,我們可以通過 VisualVM 的監視標籤對應用程式進行記憶體分析。
1)記憶體堆Heap
首先我們來看記憶體堆Heap使用情況,我本機eclipse的程序在visualVM顯示如下:
隨便寫個小程式佔用記憶體大的,執行一下
程式如下:
1 package jvisualVM; 2 3 public class JavaHeapTest { 4 public final static int OUTOFMEMORY = 200000000; 5 6 private String oom; 7 8 private int length; 9 10 StringBuffer tempOOM = new StringBuffer(); 11 12 public JavaHeapTest(int leng) { 13 this.length = leng; 14 15 int i = 0; 16 while (i < leng) { 17 i++; 18 try { 19 tempOOM.append("a"); 20 } catch (OutOfMemoryError e) { 21 e.printStackTrace(); 22 break; 23 } 24 } 25 this.oom = tempOOM.toString(); 26 27 } 28 29 public String getOom() { 30 return oom; 31 } 32 33 public int getLength() { 34 return length; 35 } 36 37 public static void main(String[] args) { 38 JavaHeapTest javaHeapTest = new JavaHeapTest(OUTOFMEMORY); 39 System.out.println(javaHeapTest.getOom().length()); 40 } 41 42 }
檢視VisualVM Monitor tab, 堆記憶體變大了
在程式執行結束之前, 點選Heap Dump 按鈕, 等待一會兒,得到dump結果,可以看到一些Summary資訊
點選Classes, 發現char[]所佔用的記憶體是最大的
雙擊它,得到如下Instances結果
Instances是按Size由大到小排列的
第一個就是最大的, 展開Field區域的 values
StringBuffer型別的 全域性變數 tempOOM 佔用記憶體特別大, 注意區域性變數是無法通過 堆dump來得到分析結果的。
另外,對於“堆 dump”來說,在遠端監控jvm的時候,VisualVM是沒有這個功能的,只有本地監控的時候才有。
###轉載註明出處:http://www.cnblogs.com/wade-xu/p/4369094.html
2)永久保留區域PermGen
其次來看下永久保留區域PermGen使用情況
執行一段類載入的程式,程式碼如下:
1 package jvisualVM; 2 3 import java.io.File; 4 import java.lang.reflect.Method; 5 import java.net.MalformedURLException; 6 import java.net.URL; 7 import java.net.URLClassLoader; 8 import java.util.ArrayList; 9 import java.util.List; 10 11 public class TestPermGen { 12 13 private static List<Object> insList = new ArrayList<Object>(); 14 15 public static void main(String[] args) throws Exception { 16 17 permLeak(); 18 } 19 20 private static void permLeak() throws Exception { 21 for (int i = 0; i < 1000; i++) { 22 URL[] urls = getURLS(); 23 URLClassLoader urlClassloader = new URLClassLoader(urls, null); 24 Class<?> logfClass = Class.forName("org.apache.commons.logging.LogFactory", true,urlClassloader); 25 Method getLog = logfClass.getMethod("getLog", String.class); 26 Object result = getLog.invoke(logfClass, "TestPermGen"); 27 insList.add(result); 28 System.out.println(i + ": " + result); 29 } 30 } 31 32 private static URL[] getURLS() throws MalformedURLException { 33 File libDir = new File("C:/Users/wadexu/.m2/repository/commons-logging/commons-logging/1.1.1"); 34 File[] subFiles = libDir.listFiles(); 35 int count = subFiles.length; 36 URL[] urls = new URL[count]; 37 for (int i = 0; i < count; i++) { 38 urls[i] = subFiles[i].toURI().toURL(); 39 } 40 return urls; 41 } 42 43 44 }
一個型別裝載之後會建立一個對應的java.lang.Class例項,這個例項本身和普通物件例項一樣儲存於堆中,我覺得之所以說是這是一種特殊的例項,某種程度上是因為其充當了訪問PermGen區域中型別資訊的代理者。
執行一段時間後拋OutOfMemoryError了, VisualVM監控結果如下:
結論:PermGen區域分配的堆空間過小,我們可以通過設定-XX: PermSize引數和-XX:MaxPermSize引數來解決。
關於PermGen OOM深入分析請參考這篇文章
關於Perform GC, 請參考這篇文章
###轉載註明出處:http://www.cnblogs.com/wade-xu/p/4369094.html
CPU分析篇
CPU 效能分析的主要目的是統計函式的呼叫情況及執行時間,或者更簡單的情況就是統計應用程式的 CPU 使用情況。
沒有程式執行時的 CPU 使用情況如下圖:
執行一段 佔用CPU 的小程式,程式碼如下
package jvisualVM; public class MemoryCpuTest { public static void main(String[] args) throws InterruptedException { cpuFix(); } /** * cpu 執行固定百分比 * * @throws InterruptedException */ public static void cpuFix() throws InterruptedException { // 80%的佔有率 int busyTime = 8; // 20%的佔有率 int idelTime = 2; // 開始時間 long startTime = 0; while (true) { // 開始時間 startTime = System.currentTimeMillis(); /* * 執行時間 */ while (System.currentTimeMillis() - startTime < busyTime) { ; } // 休息時間 Thread.sleep(idelTime); } } }
檢視監視頁面 Monitor tab
過高的 CPU 使用率可能是由於我們的專案中存在低效的程式碼;
在我們對程式施壓的時候,過低的 CPU 使用率也有可能是程式的問題。
點選取樣器Sampler, 點選“CPU”按鈕, 啟動CPU效能分析會話,VisualVM 會檢測應用程式所有的被呼叫的方法,
在CPU samples tab 下可以看到我們的方法cpufix() 的自用時間最長, 如下圖:
切換到Thread CPU Time 頁面下,我們的 main 函式這個程序 佔用CPU時間最長, 如下圖:
###轉載註明出處:http://www.cnblogs.com/wade-xu/p/4369094.html
執行緒分析篇
Java 語言能夠很好的實現多執行緒應用程式。當我們對一個多執行緒應用程式進行除錯或者開發後期做效能調優的時候,往往需要了解當前程式中所有執行緒的執行狀態,是否有死鎖、熱鎖等情況的發生,從而分析系統可能存在的問題。
在 VisualVM 的監視標籤內,我們可以檢視當前應用程式中所有活動執行緒(Live threads)和守護執行緒(Daemon threads)的數量等實時資訊。
執行一段小程式,程式碼如下:
package jvisualVM; public class MyThread extends Thread{ public static void main(String[] args) { MyThread mt1 = new MyThread("Thread a"); MyThread mt2 = new MyThread("Thread b"); mt1.setName("My-Thread-1 "); mt2.setName("My-Thread-2 "); mt1.start(); mt2.start(); } public MyThread(String name) { } public void run() { while (true) { } } }
Live threads 從11增加兩個 變成13了
Daemon threads從8增加兩個 變成10了
VisualVM 的執行緒標籤提供了三種檢視,預設會以時間線的方式展現, 如下圖:
可以看到兩個我們run的程式裡啟的執行緒:My-Thread-1 和 My-Thread-2
另外還有兩種檢視分別是表檢視和詳細資訊檢視, 這裡看一下每個Thread的詳細檢視:
###轉載註明出處:http://www.cnblogs.com/wade-xu/p/4369094.html
再來一段死鎖的程式,看VisualVM 能否分析出來
package jvisualVM; public class DeadLock { public static void main(String[] args) { Resource r1 = new Resource(); Resource r0 = new Resource(); Thread myTh1 = new LockThread1(r1, r0); Thread myTh0 = new LockThread0(r1, r0); myTh1.setName("DeadLock-1 "); myTh0.setName("DeadLock-0 "); myTh1.start(); myTh0.start(); } } class Resource { private int i; public int getI() { return i; } public void setI(int i) { this.i = i; } } class LockThread1 extends Thread { private Resource r1, r2; public LockThread1(Resource r1, Resource r2) { this.r1 = r1; this.r2 = r2; } @Override public void run() { int j = 0; while (true) { synchronized (r1) { System.out.println("The first thread got r1's lock " + j); synchronized (r2) { System.out.println("The first thread got r2's lock " + j); } } j++; } } } class LockThread0 extends Thread { private Resource r1, r2; public LockThread0(Resource r1, Resource r2) { this.r1 = r1; this.r2 = r2; } @Override public void run() { int j = 0; while (true) { synchronized (r2) { System.out.println("The second thread got r2's lock " + j); synchronized (r1) { System.out.println("The second thread got r1's lock" + j); } } j++; } } }
開啟VisualVM檢測到的JVM程序,我們可以看到這個tab在閃,VisualVM已經檢測到我這個package下面的DeadLock類出錯了
切換到Thread tab, 可以看到死鎖了, Deadlock detected!
另外可以點選Thread Dump 執行緒轉儲,進一步分析,在這裡就不贅述了,有興趣的讀者可以自行實驗。
參考文獻:
http://www.ibm.com/developerworks/cn/java/j-lo-visualvm/
遠端監控
ps:嘗試了網上的幾個網友提供的方法,始終不得其法,彙總後,終於嘗試成功!將一些需要注意的細節也記錄下來以後備用!
我們經常需要對我們的開發的軟體做各種測試, 軟體對系統資源的使用情況更是不可少, 目前有多個監控工具, 相比JProfiler對系統資源尤其是記憶體的消耗是非常龐大,JDK1.6開始自帶的VisualVM就是不錯的監控工具.
這個工具就在JAVA_HOME\bin\目錄下的jvisualvm.exe, 雙擊這個檔案就能看到一個比較直觀的介面.
要進行遠端監控, 本機的VisualVM就必須和遠端的JVM要進行通訊, Visualvm目前支援兩種remote connection方式.
分別是jstatd和JMX方式: 這裡主要介紹的是通過JMX方式.
JMX方式:
首先講述需要賬號密碼的方式:
1.修改遠端需要被監控機子的jdk配置檔案;
- 進入JAVA_HOME\jre\lib\management\
- 拷貝一份jmxremote.password.template並改名為jmxremote.password到當前目錄,並去掉# monitorRole QED 和 # controlRole R&D 這兩行 前面的註釋符號.
- 這裡是設定 遠端登入的賬號和密碼。比如預設的 賬號是 monitorRole 和controlRole,其對應的密碼分別是 QED和 R&D;可以自定義使用者名稱密碼,我自 定義了一個賬號密碼都為 root的賬號。如圖:
- 同時需要修改 當前目錄裡的jmxremote.access 檔案,該檔案可以控制訪問許可權;如圖:
2.修改遠端機器上需要被監控的程式的配置檔案 (我這裡是監控Tomcat容器內部署的應用).
修改TOMCAT_HOME/bin/catalian.sh檔案;
在第一行加入如下語句:
export JAVA_OPTS="-Djava.rmi.server.hostname=192.168.2.120 -Dcom.sun.management.jmxremote.port=18999 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=true"
ps: 我需要監控的tomcat機子ip為 192.168.2.120;監控埠設定為:18999;遠端認證設定(預設)為true;
當時就因為很多資料上沒有 將JAVA_OPTS變數 export,導致失敗了多次。
詳細的引數見下面:
引數 | 描述 |
---|---|
com.sun.management.jmxremote | true / false. Default is true |
com.sun.management.jmxremote.port | Port number. No default. |
com.sun.management.jmxremote.ssl | true / false. Default is true. |
com.sun.management.jmxremote.ssl.enabled.protocols | Default SSL/TLS protocol version. |
com.sun.management.jmxremote.ssl.enabled.cipher.suites | Default SSL/TLS cipher suites. |
com.sun.management.jmxremote.ssl.need.client.auth | true / false. Default is false |
com.sun.management.jmxremote.authenticate | true / false.Default is true |
com.sun.management.jmxremote.password.file | JRE_HOME/lib/management/jmxremote.password |
com.sun.management.jmxremote.access.file | JRE_HOME/lib/management/jmxremote.access |
com.sun.management.jmxremote.login.config | Default login configuration is a file-based password authentication |
3.重啟tomcat,讓配置生效。
ps: 可以用 netstat -anp| grep 18999 ,檢視守護執行緒是否已經啟動!如圖:
4.VisualVM配置。
- 開啟你本機命令框,輸入jvisualvm,點選這個EXE檔案;或者進入JAVA_HOME\bin\目錄下面點選也可以!
- 右擊遠端,點選新增遠端主機:
- 右擊 192.168.2.120(遠端主機),新增一個JMX連結,如圖:
- 登入後狀態:
ps:
不需要賬號密碼登入的,設定:-Dcom.sun.management.jmxremote.authenticate=false 即可。
export JAVA_OPTS="-Djava.rmi.server.hostname=192.168.2.120 -Dcom.sun.management.jmxremote.port=18999 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false" 如圖:
輸入埠,直接點選確定,即可登入!
Jstatd方法:(略)還沒有試過!
參考檔案:
https://segmentfault.com/a/1190000004313468
http://blog.csdn.net/lijiecong/article/details/6882267
http://blog.csdn.net/wzyzzu/article/details/50380511
================ End