天天看點

jvisualvm工具使用

VisualVM 是Netbeans的profile子項目,已在JDK6.0 update 7 中自帶(java啟動時不需要特定參數,監控工具在bin/jvisualvm.exe)。

一、介紹

VisualVM,能夠監控線程,記憶體情況,檢視方法的CPU時間和記憶體中的對 象,已被GC的對象,反向檢視配置設定的堆棧(如100個String對象分别由哪幾個對象配置設定出來的).

從界面上看還是比較簡潔的,左邊是樹形結構,自動顯示目前本機所運作的Java程式,還可以添加遠端的Java VM,其中括号裡面的PID指的是程序ID。OverView界面顯示VM啟動參數以及該VM對應的一些屬性。Monitor界面則是監控Java堆大小,Permgen大小,Classes和線程數量。jdk不同版本中界面會不太一緻,如有的含cpu監控,有的則不含(jdk1.6.0_10未包含)。

作用:JVM和監控的應用程式運作在不同的伺服器上,減輕應用程式的負擔,特别是HeapDump的時候,應用常能夠續負擔很大。 

VisualVM使用簡單,幾乎0配置,功能還是比較豐富的,幾乎囊括了其它JDK自帶指令的所有功能。

  • 記憶體資訊
  • 線程資訊
  • Dump堆(本地程序)
  • Dump線程(本地程序)
  • 打開堆Dump。堆Dump可以用jmap來生成。
  • 打開線程Dump
  • 生成應用快照(包含記憶體資訊、線程資訊等等)
  • 性能分析。 :idea: CPU分析(各個方法調用時間,檢查哪些方法耗時多),記憶體分析(各類對象占用的記憶體,檢查哪些類占用記憶體多)
  • ……

二、配置

本地監控不需要配置,隻要打開某個JAVA程式就會自動的加入到本地監控中。

遠端監控: 本機的VisualVM就必須和遠端的JVM要進行通信, Visualvm目前支援兩種remote connection方式,分别是jstatd和JMX方式。

遠端監控某個中間件時,需要修改中間件的啟動檔案,添加上關于jmx等的資訊。

1、遠端監控tomcat,jmx方式。

jvisualvm工具使用
伺服器 上的 tomcat 配置 jvm 啟動參數。
在 tomcat 的 catalina.bat 中添 加如下參數: 
JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=192.168.0.237    
                      -Dcom.sun.management.jmxremote.port=18999
                      -Dcom.sun.management.jmxremote.ssl=false 
                      -Dcom.sun.management.jmxremote.authenticate=false"

上述參數未設使用者名與密碼登入。      

  用戶端VisualVM配置 (我用戶端用的是WinXP).

     a.直接反鍵點選Remote,選擇Add Remote Host...

     b.在彈出的界面中輸入遠端機器的IP位址(192.168.0.237),這個IP位址會加入到Remote節點下.

     c.反鍵點選這個IP位址,選擇Add JMX Connection, 在彈出的界面中輸入剛配置的端口号(18999), 這個連接配接會加入到該IP節點下.

     d.反鍵點選這個連接配接,選擇Open.

jvisualvm工具使用

此處參數設定與jconsole工具遠端監控tomcat相同。

2、監視websphere伺服器JVM的配置

 http://xjsunjie.blog.51cto.com/999372/1331880

jconsole & jvisualvm遠端監視websphere伺服器JVM的配置案

 在啟動檔案裡配置。

3、jstatd 配置。

三、使用

http://zhouanya.blog.51cto.com/4944792/1370017

四、使用執行個體

1、插件“Visual GC"使用。

轉自:http://supercharles888.blog.51cto.com/609344/1179790

安裝 ”Visual GC"插件:

這個插件是jvisualvm的插件,它非常強大,可以動态的對指定的程序進行監控,并且來通過統計面闆來分類顯示出各項任務/事件的總時間開銷:

安裝方法: Tool->Plugin->Available Plugins:

jvisualvm工具使用

重新開機Visual VM 之後,就可以看到這個"Visual GC"已經被正确的顯示了。

實戰: 用Visual VM和Visual GC來優化我們的Eclipse啟動:

首先,我們啟動eclipse:

在ps -ef|grep eclipse(windows則是任務管理器)中,我們可以從看到這個程序id為32561

我們從Visual VM中找到對應的process id:

jvisualvm工具使用

我們切換到 “Visual GC"标簽頁,它會顯示啟動eclipse的所有測量資料:

jvisualvm工具使用

分析:

從上圖中我們可以很明顯的看出來,主要的時間開銷在以下2方面:

(1)編譯時間有點長,用了3.794秒,這個時間主要是用來校驗eclipse平台本身的位元組碼了,是以我們需要關閉位元組碼校驗,讓啟動時候不會去校驗平台本身(也是java寫的)的位元組碼,為了達到這個目的,我們隻需要在eclipse啟動參數中加上-Xverify:none

如下所示,因為我們用的是Spring Source Tool Suite,是以我們在STS.ini中增加這一行。

jvisualvm工具使用

(2)另外一個大問題就是類加載時間,它有2部分組成,因為類有2部分組成,一是eclipse平台自帶的類,二是它所使用的插件的類檔案,我們可以在eclipse啟動的時候關閉不必要的插件加載來減少類加載時間,方法是Preference->General->Startup and Shutdown

jvisualvm工具使用

校驗結果:

現在我們把eclipse關閉并且重新打開,這會啟動一個新的程序,id為32696,我們把這次Visual GC的測量圖和原來的進行比較:

jvisualvm工具使用

從這裡可以看出來,時間被明顯的縮短了,編譯時間從3.794秒縮短到2.155秒,提升百分比為43.1%。而類加載時間從18.424秒縮短到10.208秒,提升百分比為44.6%。

額外步驟:

對于一些其他的啟動參數,比如初始記憶體,最大記憶體,Gem,Perm的參數。

2、将dump的檔案,使用其進行檢視。

1)dump檔案。

jvisualvm工具使用

2)将dump檔案傳至jvisualvm本機,點選”檔案“-》”裝入“,選擇第一步生成的dump檔案。

a.摘要标簽

jvisualvm工具使用

可檢視dump的各項資訊。

b.類 标簽

jvisualvm工具使用

輕按兩下某個類,點選執行個體視圖。

c.執行個體試圖

五、遠端監控記憶體洩露、解決記憶體溢出問題

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

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

異:

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

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

2)監測記憶體洩漏

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

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

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

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

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

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

3)Heap dump 分析

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

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

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

對比上面三個截圖,發現似乎有個東西在急速飙升,仔細一看是這個對象: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)那麼就會産生此錯誤資訊了。

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

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的消耗。

如何避免記憶體洩漏、溢出

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&id=3747  

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

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

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

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

7) 優化配置。

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

b.設定NewSize、MaxNewSize相等;

c.設定Heap size, PermGen space:

六、使用過程中遇到的一些問題與疑問

問題1:從伺服器dump堆記憶體後檔案比較大(3.5G左右),加載檔案、檢視執行個體對象都很慢,還提示配置xmx大小。在windows上如何配置xmx大小?

表明給VisualVM配置設定的堆記憶體不夠,找到${visualvm}/etc/visualvm.conf (如:C:\Program Files\Java\jdk1.6.0_10\lib\visualvm\etc)這個檔案,修改

default_options=”-J-Xms24m -J-Xmx192m“

default_options=”-J-Xms24m -J-Xmx1024m”(

再重新開機VisualVM就行了。

對于“堆 dump”來說,在遠端監控jvm的時候,VisualVM是沒有這個功能的,隻有本地監控的時候才有。另外,就算是本地監控,它在dump和得到執行個體的 速度那是相當的慢的。是以鑒于這幾個原因,不建議用VisualVM,而是用jmap加上Mat來分析記憶體情況。

問題2: