轉自:http://www.cnblogs.com/wade-xu/p/4369094.htmlhtml
這篇總結的很不錯(本人親自操手學習),留着之後複習備用,很適合入門級的學習者:java
VisualVM 是一款免費的,集成了多個 JDK 命令行工具的可視化工具,它能爲您提供強大的分析能力,對 Java 應用程序作性能分析和調優。這些功能包括生成和分析海量數據、跟蹤內存泄漏、監控垃圾回收器、執行內存和 CPU 分析,同時它還支持在 MBeans 上進行瀏覽和操做。本文主要介紹如何使用 VisualVM 進行性能分析及調優。web
準備工做
自從 JDK 6 Update 7 之後已經做爲 Oracle JDK 的一部分,位於 JDK 根目錄的 bin 文件夾下,無需安裝,直接運行便可。apache
內存分析篇小程序
VisualVM 經過檢測 JVM 中加載的類和對象信息等幫助咱們分析內存使用狀況,咱們能夠經過 VisualVM 的監視標籤對應用程序進行內存分析。多線程
1)內存堆Heap
首先咱們來看內存堆Heap使用狀況,我本機eclipse的進程在visualVM顯示以下:app
隨便寫個小程序佔用內存大的,運行一下eclipse
程序以下:jvm
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, 堆內存變大了ide
在程序運行結束以前, 點擊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/