天天看點

Jvisualvm--JAVA性能分析工具 1.安裝 2使用

JDK自帶的JAVA性能分析工具。它已經在你的JDK bin目錄裡了,隻要你使用的是JDK1.6 Update7之後的版本。點選一下jvisualvm.exe圖示它就可以運作了。

這裡是VisualVM 的官方網站:https://visualvm.dev.java.net,資料很全,同時提供VisualVM最近版本下載下傳。

隻要安裝JDK即可,運作jvisualvm.exe ,選擇【工具】——【插件】——【可用插件】

要從遠端應用程式中檢索資料,需要在遠端 JVM 上運作 jstatd 實用程式。即要進行以下操作:

1)在jdk 安裝目錄的bin目錄下建立檔案jstatd.all.policy,檔案内容為:  

grant codebase "file:${java.home}/../lib/tools.jar" {

permission java.security.AllPermission;

};

2)再建立檔案jstatd.sh ,檔案内容為:

./jstatd -J-Djava.security.policy=jstatd.all.policy

3)啟動jstat : nohup jstatd.sh & (預設啟動端口為1099)

4)配置resin.conf,把以下注釋打開:

   <!-- no use args

     <jvm-arg>-Xdebug</jvm-arg>

     <jvm-arg>-Dcom.sun.management.jmxremote</jvm-arg>

<a href="http://s3.51cto.com/wyfs02/M02/22/4B/wKioL1MZs6mSYFZyAAEP56Xo1Tw306.jpg" target="_blank"></a>

1)在應用的resin配置檔案中加配置:

&lt;jvm-arg&gt;-Dcom.sun.management.jmxremote&lt;/jvm-arg&gt;

&lt;jvm-arg&gt;-Dcom.sun.management.jmxremote.port=9009&lt;/jvm-arg&gt;

&lt;jvm-arg&gt;-Dcom.sun.management.jmxremote.ssl=false&lt;/jvm-arg&gt;

&lt;jvm-arg&gt;-Dcom.sun.management.jmxremote.authenticate=false&lt;/jvm-arg&gt;

2)選擇Tools菜單裡的Plugins菜單,點選 'VisualVM-JConsole' 然後點選'Install' 按鈕

3)安裝完畢後重新啟動VisualVm

4)配置JConsole頁簽,點選'JConsole Plugins'按鈕

5)點選'Add JAR/Folder'按鈕,

6)添加JDK_HOME/demo/management/JTop/JTop.jar

7)重新打開監控頁面,可以看到JConsole正常工作

<a href="http://s3.51cto.com/wyfs02/M01/22/4A/wKiom1MZs_mgO1XbAAJmd4w8Pqc594.jpg" target="_blank"></a>

點采樣器欄:

<a href="http://s3.51cto.com/wyfs02/M00/22/4B/wKioL1MZs_HzhnGDAAGh15q1Mts787.jpg" target="_blank"></a>

點選cpu,觀察到cpu使用狀況,點選snapshot,采取結果,可以選擇檢視方法、類或包的cpu使用情況,使用工具可以查找想要檢視的方法、類或包:

<a href="http://s3.51cto.com/wyfs02/M01/22/4B/wKioL1MZtBzBJoWQAAF75iFVDSA718.jpg" target="_blank"></a>

1)選擇要監控的pid,檢視GC情況

<a href="http://s3.51cto.com/wyfs02/M02/22/4B/wKioL1MZtFPBa-3qAAH24ueOTx8191.jpg" target="_blank"></a>

如果Old區滿了,每次回收都很少或者回收不了,說明GC有問題。

1)記憶體洩露、溢出的異同

同:都會導緻應用程式運作出現問題,性能下降或挂起。

異:

v記憶體洩露是導緻記憶體溢出的原因之一;記憶體洩露積累起來将導緻記憶體溢出。

v記憶體洩露可以通過完善代碼來避免;記憶體溢出可以通過調整配置來減少發生頻率,但無法徹底避免。

2)監測記憶體洩漏

·記憶體洩漏是指程式中間動态配置設定了記憶體,但在程式結束時沒有釋放這部分記憶體,進而造成那部分記憶體不可用的情況,重新開機計算機可以解決,但也有可能再次發生記憶體洩露,記憶體洩露和硬體沒有關系,它是由軟體設計缺陷引起的。  

·記憶體洩漏可以分為4類:

a. 常發性記憶體洩漏。發生記憶體洩漏的代碼會被多次執行到,每次被執行的時候都會導緻一塊記憶體洩漏。

b.偶發性記憶體洩漏。發生記憶體洩漏的代碼隻有在某些特定環境或操作過程下才會發生。常發性和偶發性是相對的。對于特定的環境,偶發性的也許就變成了常發性的。是以測試環境和測試方法對檢測記憶體洩漏至關重要。

c.一次性記憶體洩漏。發生記憶體洩漏的代碼隻會被執行一次,或者由于算法上的缺陷,導緻總會有一塊僅且一塊記憶體發生洩漏。比如,在類的構造函數中配置設定記憶體,在析構函數中卻沒有釋放該記憶體,是以記憶體洩漏隻會發生一次。

d.隐式記憶體洩漏。程式在運作過程中不停的配置設定記憶體,但是直到結束的時候才釋放記憶體。嚴格的說這裡并沒有發生記憶體洩漏,因為最終程式釋放了所有申請的記憶體。但是對于一個伺服器程式,需要運作幾天,幾周甚至幾個月,不及時釋放記憶體也可能導緻最終耗盡系統的所有記憶體。是以,我們稱這類記憶體洩漏為隐式記憶體洩漏。

3)Heap dump 分析

每隔一段時間給所檢測的java應用做一次heap dump:

<a href="http://s3.51cto.com/wyfs02/M01/22/4A/wKiom1MZtKTTJ5K3AABJbkv4_d4875.jpg" target="_blank"></a>

(或者在響應應用pid上滑鼠右鍵heap dump)彈出以下提示框:

<a href="http://s3.51cto.com/wyfs02/M00/22/4B/wKioL1MZtKaiSdicAAB6rwX6AqE541.jpg" target="_blank"></a>

在應用伺服器将此檔案下載下傳到jvisual vm所在的機器上,file--load打開此檔案,如下面三圖所示:

<a href="http://s3.51cto.com/wyfs02/M02/22/4A/wKiom1MZtSiQgvHBAAF6Dw_4JBI844.jpg" target="_blank"></a>

<a href="http://s3.51cto.com/wyfs02/M01/22/4B/wKioL1MZtQLDKyaOAAO9bvzBju0083.jpg" target="_blank"></a>

<a href="http://s3.51cto.com/wyfs02/M00/22/4A/wKiom1MZtSixQ5WVAAGs96BTilg611.jpg" target="_blank"></a>

對比上面三個截圖,發現似乎有個東西在急速飙升,仔細一看是這個對象:org.eclipse.swt.graphics.Image 在第一章圖中這個還沒排的上,第二次dump已經上升到5181個,第三次就是7845個了。漲速相當快,而且和任務管理器裡面看到的GDI數量增漲一緻,就是它了。

問題到這兒就比較清楚了,回到代碼裡面仔細一看可以發現,是某個地方反複的用圖檔來建立Image對象導緻的,改掉以後搞定問題。

到這裡其實我想說的是,Java使用起來其實要比C++更容易導緻記憶體洩漏。對于C++來說,每一個申請的對象都必須明确釋放,任何沒有釋放的對象都會導緻memleak,這是不可饒恕的,而且這類問題已經有很多工具和方法來解決。但是到了Java裡面情況就不同了,對于Java程式員來說對象都是不需要也無法主動銷毀的,是以一般的思路是:随用随new,用完即丢。很多對象具體的生命周期可能連寫代碼的人自己也不清楚,或者不需要清楚,隻知道某個時刻垃圾收集器會去做的,不用管。但很可能某個對象在“用完即丢”的時候在另一個不容易發現的地方被儲存了一個引用,那麼這個對象就永遠不會被回收。更加糟糕的是整個程式從設計之初就沒有考慮過對象回收的問題,對于C++程式員來說memleak必然是一個設計錯誤,但是對Java程式員來說這隻是一個疏忽,而且似乎沒有什麼好的辦法來避免。今天看到的這個問題是因為GDI洩漏會造成嚴重後果才被重視,但如果僅僅是造成記憶體洩漏,那這個程式可能得連續跑上個十天半個月才會發現問題。最後就是,對于c++,錯誤的代碼在測試階段就可以快速的偵測出哪怕一個byte的memleak并加以改正,但是對于java程式,理論上沒有哪個工具能夠知道是不是有洩漏,因為除了作者自己以外沒有人能夠知道一個被引用的對象是不是應該被銷毀,隻有通過大量的,長期的壓力測試才能發現問題,這是很危險的一件事情。

4)解決記憶體溢出問題

1、java.lang.OutOfMemoryError: PermGen space

JVM管理兩種類型的記憶體,堆和非堆。堆是在JVM啟動時建立;非堆是留給JVM自己用的,用來存放類的資訊的。它和堆不同,運作期内GC不會釋放空間。如果web app用了大量的第三方jar或者應用有太多的class檔案而恰好MaxPermSize設定較小,超出了也會導緻這塊記憶體的占用過多造成溢出,或者tomcat熱部署時侯不會清理前面加載的環境,隻會将context更改為新部署的,非堆存的内容就會越來越多。

PermGen space的全稱是Permanent Generation space,是指記憶體的永久儲存區域,這塊記憶體主要是被JVM存放Class和Meta資訊的,Class在被Loader時就會被放到PermGen space中,它和存放類執行個體(Instance)的Heap區域不同,GC(Garbage Collection)不會在主程式運作期對PermGen space進行清理,是以如果你的應用中有很CLASS的話,就很可能出現PermGen space錯誤,這種錯誤常見在web伺服器對JSP進行pre compile的時候。如果你的WEB APP下都用了大量的第三方jar, 其大小超過了jvm預設的大小(4M)那麼就會産生此錯誤資訊了。

<a href="http://s3.51cto.com/wyfs02/M01/22/4B/wKioL1MZtlHREhrhAAMFKXoTEMw761.jpg" target="_blank"></a>

如上圖所示,PermGen在程式運作一段時間後超出了我們指定的128MB,通過Classes視圖看到,Java在運作的同時加載了大量的類到記憶體中。PermGen會存儲Jar或者Class的描述資訊;是以在class大量增加的同時PermGen超出了我們指定的範圍。為了讓應用穩定,我們需要探尋新的PermGen範圍。檢測時段時候後(如下圖)發現PermGen在145MB左右穩定,那麼我們就得到了穩定的新參數;這樣PermGen記憶體溢出的問題得到解決。

<a href="http://s3.51cto.com/wyfs02/M01/22/4A/wKiom1MZtrWRVGIQAAMV1WiIwr0717.jpg" target="_blank"></a>

2、java.lang.OutOfMemoryError: Java heap space

第一種情況是個補充,主要存在問題就是出現在這個情況中。其預設空間(即-Xms)是實體記憶體的1/64,最大空間(-Xmx)是實體記憶體的1/4。如果記憶體剩餘不到40%,JVM就會增大堆到Xmx設定的值,記憶體剩餘超過70%,JVM就會減小堆到Xms設定的值。是以伺服器的Xmx和Xms設定一般應該設定相同避免每次GC後都要調整虛拟機堆的大小。假設實體記憶體無限大,那麼JVM記憶體的最大值跟作業系統有關,一般32位機是1.5g到3g之間,而64位的就不會有限制了。

注意:如果Xms超過了Xmx值,或者堆最大值和非堆最大值的總和超過了實體記憶體或者作業系統的最大限制都會引起伺服器啟動不起來。

垃圾回收GC的角色,JVM調用GC的頻度還是很高的,主要兩種情況下進行垃圾回收:

一個是當應用程式線程空閑;另一個是java記憶體堆不足時,會不斷調用GC,若連續回收都解決不了記憶體堆不足的問題時,就會報out of memory錯誤。因為這個異常根據系統運作環境決定,是以無法預期它何時出現。

根據GC的機制,程式的運作會引起系統運作環境的變化,增加GC的觸發機會。

為了避免這些問題,程式的設計和編寫就應避免垃圾對象的記憶體占用和GC的開銷。顯示調用System.GC()隻能建議JVM需要在記憶體中對垃圾對象進行回收,但不是必須馬上回收。一個是并不能解決記憶體資源耗空的局面,另外也會增加GC的消耗。

<a href="http://s3.51cto.com/wyfs02/M01/22/4A/wKiom1MZuOfBZE4hAAJS5vcnocE082.jpg" target="_blank"></a>

1)盡早釋放無用對象的引用。

好的辦法是使用臨時變量的時候,讓引用變量在退出活動域後自動設定為null,暗示垃圾收集器來收集該對象,防止發生記憶體洩露。

2) 程式進行字元串處理時,盡量避免使用String,而應使用StringBuffer。

因為每一個String對象都會獨立占用記憶體一塊區域,如:

1.String str = "aaa";    

2.String str2 = "bbb";    

3.String str3 = str + str2;    

4.// 假如執行此次之後str , str2再不被調用,那麼它們就會在記憶體中等待GC回收;    

5.// 假如程式中存在過多的類似情況就會出現記憶體錯誤;  

3) 盡量少用靜态變量。

因為靜态變量是全局的,GC不會回收。

4) 避免集中建立對象尤其是大對象,如果可以的話盡量使用流操作。

JVM會突然需要大量記憶體,這時會觸發GC優化系統記憶體環境; 一個案例如下:

1.// 使用jspsmartUpload作檔案上傳,運作過程中經常出現java.outofMemoryError的錯誤,    

2.// 檢查之後發現問題:元件裡的代碼    

3.m_totalBytes = m_request.getContentLength();    

4.m_binArray = new byte[m_totalBytes];    

5.// totalBytes這個變量得到的數極大,導緻該數組配置設定了很多記憶體空間,而且該數組不能及時釋放。    

6.// 解決辦法隻能換一種更合适的辦法,至少是不會引發outofMemoryError的方式解決。    

7.// 參考:http://bbs.xml.org.cn/blog/more.asp?name=hongrui&amp;id=3747  

5) 盡量運用對象池技術以提高系統性能。

生命周期長的對象擁有生命周期短的對象時容易引發記憶體洩漏,例如大集合對象擁有大資料量的業務對象的時候,可以考慮分塊進行處理,然後解決一塊釋放一塊的政策。

6) 不要在經常調用的方法中建立對象,尤其是忌諱在循環中建立對象。

可以适當的使用hashtable,vector 建立一組對象容器,然後從容器中去取那些對象,而不用每次new之後又丢棄。

7) 優化配置。

a.設定-Xms、-Xmx相等;

b.設定NewSize、MaxNewSize相等;

c.設定Heap size, PermGen space:

本文轉自 32氪 51CTO部落格,原文連結:http://blog.51cto.com/10672221/1944898

下一篇: C# 腳本