使用 VisualVM 進(jìn)行性能分析及調(diào)優(yōu)

轉(zhuǎn)自:http://www.cnblogs.com/wade-xu/p/4369094.html

這篇總結(jié)的很不錯(cuò)(本人親自操手學(xué)習(xí)),留著以后復(fù)習(xí)備用,很適合入門級(jí)的學(xué)習(xí)者:

VisualVM 是一款免費(fèi)的,集成了多個(gè) JDK 命令行工具的可視化工具,它能為您提供強(qiáng)大的分析能力,對(duì) Java 應(yīng)用程序做性能分析和調(diào)優(yōu)。這些功能包括生成和分析海量數(shù)據(jù)、跟蹤內(nèi)存泄漏、監(jiān)控垃圾回收器、執(zhí)行內(nèi)存和 CPU 分析,同時(shí)它還支持在 MBeans 上進(jìn)行瀏覽和操作。本文主要介紹如何使用 VisualVM 進(jìn)行性能分析及調(diào)優(yōu)。

準(zhǔn)備工作

自從 JDK 6 Update 7 以后已經(jīng)作為 Oracle JDK 的一部分,位于 JDK 根目錄的 bin 文件夾下,無需安裝,直接運(yùn)行即可。

?內(nèi)存分析篇

VisualVM 通過檢測 JVM 中加載的類和對(duì)象信息等幫助我們分析內(nèi)存使用情況,我們可以通過 VisualVM 的監(jiān)視標(biāo)簽對(duì)應(yīng)用程序進(jìn)行內(nèi)存分析。

1)內(nèi)存堆Heap

首先我們來看內(nèi)存堆Heap使用情況,我本機(jī)eclipse的進(jìn)程在visualVM顯示如下:

隨便寫個(gè)小程序占用內(nèi)存大的,運(yùn)行一下

程序如下:

1package jvisualVM; 2 3publicclass JavaHeapTest { 4publicfinalstaticintOUTOFMEMORY = 200000000; 5 6private String oom; 7 8privateint length; 910StringBuffer tempOOM =new StringBuffer();1112publicJavaHeapTest(int leng) {13this.length = leng;1415inti = 0;16while(i < leng) {17i++;18try {19tempOOM.append("a");20}catch (OutOfMemoryError e) {21? ? ? ? ? ? ? e.printStackTrace();22break;23? ? ? ? ? ? }24? ? ? ? }25this.oom = tempOOM.toString();2627? ? }2829public String getOom() {30return oom;31? ? }3233publicint getLength() {34return length;35? ? }3637publicstaticvoid main(String[] args) {38JavaHeapTest javaHeapTest =new JavaHeapTest(OUTOFMEMORY);39? ? ? ? System.out.println(javaHeapTest.getOom().length());40? ? }4142}

查看VisualVM Monitor tab, 堆內(nèi)存變大了

在程序運(yùn)行結(jié)束之前, 點(diǎn)擊Heap Dump 按鈕, 等待一會(huì)兒,得到dump結(jié)果,可以看到一些Summary信息

點(diǎn)擊Classes,?發(fā)現(xiàn)char[]所占用的內(nèi)存是最大的

雙擊它,得到如下Instances結(jié)果

Instances是按Size由大到小排列的

第一個(gè)就是最大的, 展開Field區(qū)域的 values

StringBuffer類型的 全局變量 tempOOM 占用內(nèi)存特別大, 注意局部變量是無法通過 堆dump來得到分析結(jié)果的。

另外,對(duì)于“堆 dump”來說,在遠(yuǎn)程監(jiān)控jvm的時(shí)候,VisualVM是沒有這個(gè)功能的,只有本地監(jiān)控的時(shí)候才有。

###轉(zhuǎn)載注明出處:http://www.cnblogs.com/wade-xu/p/4369094.html


2)永久保留區(qū)域PermGen

其次來看下永久保留區(qū)域PermGen使用情況

運(yùn)行一段類加載的程序,代碼如下:

1package jvisualVM; 2 3import java.io.File; 4import java.lang.reflect.Method; 5import java.net.MalformedURLException; 6import java.net.URL; 7import java.net.URLClassLoader; 8import java.util.ArrayList; 9import java.util.List;1011publicclass TestPermGen {1213privatestaticList insList =newArrayList();1415publicstaticvoidmain(String[] args)throws Exception {1617? ? ? ? permLeak();18? ? }1920privatestaticvoidpermLeak()throws Exception {21for(inti = 0; i < 1000; i++) {22URL[] urls = getURLS();23URLClassLoader urlClassloader =newURLClassLoader(urls,null);24Class logfClass = Class.forName("org.apache.commons.logging.LogFactory",true,urlClassloader);25Method getLog = logfClass.getMethod("getLog", String.class);26Object result = getLog.invoke(logfClass, "TestPermGen");27? ? ? ? ? ? insList.add(result);28System.out.println(i + ": " + result);29? ? ? ? }30? ? }3132privatestaticURL[] getURLS()throws MalformedURLException {33File libDir =newFile("C:/Users/wadexu/.m2/repository/commons-logging/commons-logging/1.1.1");34File[] subFiles = libDir.listFiles();35intcount = subFiles.length;36URL[] urls =new URL[count];37for(inti = 0; i < count; i++) {38urls[i] = subFiles[i].toURI().toURL();39? ? ? ? }40return urls;41? ? }424344}

一個(gè)類型裝載之后會(huì)創(chuàng)建一個(gè)對(duì)應(yīng)的java.lang.Class實(shí)例,這個(gè)實(shí)例本身和普通對(duì)象實(shí)例一樣存儲(chǔ)于堆中,我覺得之所以說是這是一種特殊的實(shí)例,某種程度上是因?yàn)槠涑洚?dāng)了訪問PermGen區(qū)域中類型信息的代理者。

?運(yùn)行一段時(shí)間后拋OutOfMemoryError了, VisualVM監(jiān)控結(jié)果如下:

結(jié)論:PermGen區(qū)域分配的堆空間過小,我們可以通過設(shè)置-XX: PermSize參數(shù)和-XX:MaxPermSize參數(shù)來解決。

關(guān)于PermGen OOM深入分析請(qǐng)參考這篇文章

關(guān)于Perform GC, 請(qǐng)參考這篇文章

###轉(zhuǎn)載注明出處:http://www.cnblogs.com/wade-xu/p/4369094.html


CPU分析篇

CPU 性能分析的主要目的是統(tǒng)計(jì)函數(shù)的調(diào)用情況及執(zhí)行時(shí)間,或者更簡單的情況就是統(tǒng)計(jì)應(yīng)用程序的 CPU 使用情況。

沒有程序運(yùn)行時(shí)的 CPU 使用情況如下圖:


運(yùn)行一段 占用CPU 的小程序,代碼如下

package jvisualVM;publicclass MemoryCpuTest {

? ? publicstaticvoidmain(String[] args)throws InterruptedException {

? ? ? ? cpuFix();

? ? }

? ? /**? ? * cpu 運(yùn)行固定百分比

? ? *

? ? * @throws InterruptedException

? ? */publicstaticvoidcpuFix()throws InterruptedException {

? ? ? ? // 80%的占有率intbusyTime = 8;

? ? ? ? // 20%的占有率intidelTime = 2;

? ? ? ? // 開始時(shí)間longstartTime = 0;


? ? ? ? while(true) {

? ? ? ? ? ? // 開始時(shí)間startTime = System.currentTimeMillis();


? ? ? ? ? ? /*? ? ? ? ? ? * 運(yùn)行時(shí)間

? ? ? ? ? ? */while(System.currentTimeMillis() - startTime < busyTime) {

? ? ? ? ? ? ? ? ;

? ? ? ? ? ? }


? ? ? ? ? ? // 休息時(shí)間? ? ? ? ? ? Thread.sleep(idelTime);

? ? ? ? }

? ? }

}

查看監(jiān)視頁面 Monitor tab


過高的 CPU 使用率可能是由于我們的項(xiàng)目中存在低效的代碼;

在我們對(duì)程序施壓的時(shí)候,過低的 CPU 使用率也有可能是程序的問題。


點(diǎn)擊取樣器Sampler, 點(diǎn)擊“CPU”按鈕, 啟動(dòng)CPU性能分析會(huì)話,VisualVM 會(huì)檢測應(yīng)用程序所有的被調(diào)用的方法,

在CPU samples tab 下可以看到我們的方法cpufix() 的自用時(shí)間最長, 如下圖:

切換到Thread CPU Time 頁面下,我們的 main 函數(shù)這個(gè)進(jìn)程 占用CPU時(shí)間最長, 如下圖:

###轉(zhuǎn)載注明出處:http://www.cnblogs.com/wade-xu/p/4369094.html

線程分析篇

Java 語言能夠很好的實(shí)現(xiàn)多線程應(yīng)用程序。當(dāng)我們對(duì)一個(gè)多線程應(yīng)用程序進(jìn)行調(diào)試或者開發(fā)后期做性能調(diào)優(yōu)的時(shí)候,往往需要了解當(dāng)前程序中所有線程的運(yùn)行狀態(tài),是否有死鎖、熱鎖等情況的發(fā)生,從而分析系統(tǒng)可能存在的問題。

在 VisualVM 的監(jiān)視標(biāo)簽內(nèi),我們可以查看當(dāng)前應(yīng)用程序中所有活動(dòng)線程(Live threads)和守護(hù)線程(Daemon threads)的數(shù)量等實(shí)時(shí)信息。


運(yùn)行一段小程序,代碼如下:

package jvisualVM;publicclassMyThreadextends Thread{


? ? publicstaticvoid main(String[] args) {


? ? ? ? MyThread mt1 =newMyThread("Thread a");

? ? ? ? MyThread mt2 =newMyThread("Thread b");


? ? ? ? mt1.setName("My-Thread-1 ");

? ? ? ? mt2.setName("My-Thread-2 ");


? ? ? ? mt1.start();

? ? ? ? mt2.start();

? ? }


? ? public MyThread(String name) {

? ? }

? ? publicvoid run() {


? ? ? ? while(true) {


? ? ? ? }

? ? }


}

Live threads 從11增加兩個(gè) 變成13了

Daemon threads從8增加兩個(gè) 變成10了?


VisualVM 的線程標(biāo)簽提供了三種視圖,默認(rèn)會(huì)以時(shí)間線的方式展現(xiàn), 如下圖:

可以看到兩個(gè)我們r(jià)un的程序里啟的線程:My-Thread-1 和?My-Thread-2


另外還有兩種視圖分別是表視圖和詳細(xì)信息視圖, 這里看一下每個(gè)Thread的詳細(xì)視圖:

###轉(zhuǎn)載注明出處:http://www.cnblogs.com/wade-xu/p/4369094.html

?再來一段死鎖的程序,看VisualVM 能否分析出來

package jvisualVM;publicclass DeadLock {

? ? publicstaticvoid 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 {

? ? ? ? privateint i;


? ? ? ? publicint getI() {

? ? ? ? ? ? return i;

? ? ? ? }


? ? ? ? publicvoidsetI(int i) {

? ? ? ? ? ? this.i = i;

? ? ? ? }


? ? }

? ? classLockThread1extends Thread {

? ? ? ? private Resource r1, r2;


? ? ? ? public LockThread1(Resource r1, Resource r2) {

? ? ? ? ? ? this.r1 = r1;

? ? ? ? ? ? this.r2 = r2;

? ? ? ? }


? ? ? ? @Override

? ? ? ? publicvoid run() {

? ? ? ? ? ? intj = 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++;

? ? ? ? ? ? }

? ? ? ? }


? ? }

? ? classLockThread0extends Thread {

? ? ? ? private Resource r1, r2;


? ? ? ? public LockThread0(Resource r1, Resource r2) {

? ? ? ? ? ? this.r1 = r1;

? ? ? ? ? ? this.r2 = r2;

? ? ? ? }


? ? ? ? @Override

? ? ? ? publicvoid run() {

? ? ? ? ? ? intj = 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進(jìn)程,我們可以看到這個(gè)tab在閃,VisualVM已經(jīng)檢測到我這個(gè)package下面的DeadLock類出錯(cuò)了

切換到Thread tab, 可以看到死鎖了,?Deadlock detected!

另外可以點(diǎn)擊Thread Dump 線程轉(zhuǎn)儲(chǔ),進(jìn)一步分析,在這里就不贅述了,有興趣的讀者可以自行實(shí)驗(yàn)。


參考文獻(xiàn):

http://www.ibm.com/developerworks/cn/java/j-lo-visualvm/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • VisualVM 是一款免費(fèi)的,集成了多個(gè) JDK 命令行工具的可視化工具,它能為您提供強(qiáng)大的分析能力,對(duì) Jav...
    Ag劉曉婷閱讀 883評(píng)論 0 1
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,623評(píng)論 18 399
  • Xmas * 特別限量版 這一抹紅,驚艷了時(shí)光
    URoom一室一品閱讀 170評(píng)論 0 0
  • 未裝修完的運(yùn)動(dòng)場,跑道一層一層地被剝開,瀝青的填充裸露在外——“一個(gè)現(xiàn)代病的代言人,只有剛鋪上才會(huì)有被燒焦的味道。...
    十七秒閱讀 325評(píng)論 1 2

友情鏈接更多精彩內(nèi)容