Java自己提供了多種豐富的方法和工具來幫助開發人員查看和分析GC及JVM內存的情況,同時開源界和商業界也有一些工具可用於查看、分析GC及JVM內存的情況。經過這些分析,能夠排查程序中內存泄露的問題及調優程序的性能。下面介紹幾種經常使用的免費工具,其中知名的有JProfiler 等。java
1. 輸出GC日誌linux
輸出GC日誌對於跟蹤分析GC的情況來講,無疑是最直接地分析內存回收情況的方法,只是GC日誌輸出後須要人爲地進行分析,以判斷GC的情況。數據庫
JVM支持將日誌輸出到控制檯或指定的文件中,方法有以下幾種。瀏覽器
輸出到控制檯eclipse
在JVM的啓動參數中加入-XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime,按照參數的順序分別輸出GC的簡要信息,GC的詳細信息、GC的時間信息及GC形成的應用暫停的時間。jvm
輸出到指定的文件ide
在1中的jvm啓動參數中再增長-Xloggc: gc.log可指定將gc的信息輸出到gc.log中。工具
可用於GC跟蹤分析的參數還有-verbose:gc、-XX:+PrintTenuringDistribution等。性能
2. GC Portal測試
將GC日誌輸出當然有必定的做用,但若是要靠人爲進行分析,仍是至關複雜的。所以Sun提供了一個GC Portal來幫助分析這些GC日誌,並生成相關的圖形化的報表,GC Portal部署起來會有些麻煩,它須要運行在老版本的Tomcat上,同時須要數據庫,部署完畢後經過上傳日誌文件的方式便可完成GC日誌的分析,此GC日誌輸出的JVM參數爲:-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps [-Xloggc:文件名],在上傳日誌時GC Portal的選項裏只有jdk 1.2或jdk 1.2-1.4的版本。雖然通過測試,JDK 6的日誌也是能夠分析出來的,但它的限制在於僅支持5MB的gc日誌的分析,GC Portal可提供吞吐量的分析、耗費的CPU的時間、形成的應用暫停的時間、每秒重新生代轉化到舊生代的數量、minor GC的情況及Full GC的情況等,如圖1所示。
圖1 GCPortal示例
GC Portal中還有一個頗有用的部分是提供調整GC參數的預測,例如能夠選擇給young size增長20%的空間。GC Portal會根據當前的日誌信息來評估在調整參數後的運行效果,不必定很準確,但畢竟能帶來一些參考意義。
3. JConsole
JConsole能夠圖形化查看JVM中內存的變化情況,JConsole是JDK 5及以上版本中自帶的工具,位於JDK的bin目錄下,運行時直接運行JConsole.exe或JConsole.sh(要求支持圖形界面)。在本地的Tab頁上看到運行了java的pid,雙擊便可查看相應進程的JVM情況,同時,JConsole也支持查看遠程的JVM的運行情況,具體可參見JConsole的User Guide。
JConsole中顯示了JVM中不少的信息:內存、線程、類和MBean等,在打開JConsole的內存Tab頁後,可看到JVM內存部分的運行情況。這對於分析內存是否有溢出及GC的效果更加直接明瞭,JConsole的運行效果如圖2所示。
圖2 JConsole運行效果
4. JVisualVM
JVisualVM是JDK 6 update 7以後推出的一個工具,它相似於JProfiler的工具,基於此工具可查看內存的消耗狀況、線程的執行情況及程序中消耗CPU、內存的動做。
在內存分析上,JVisualVM的最大好處是可經過安裝VisualGC插件來分析GC趨勢、內存消耗詳細情況。
VisualGC的運行如圖3所示。
圖3 VisualGC運行效果
從圖3中可看到各區的內存消耗情況及GC Time的圖表,其提供的Histogram視圖對於調優也有很大幫助。
基於JVisualVM的Profiler中的Memory還可查看對象佔用內存的情況,如圖4所示。
4. JMap
JMap是JDK中自帶的一個用於分析JVM內存情況的工具,位於JDK的bin目錄下。使用JMap可查看目前JVM中各個代的內存情況、JVM中對象的內存的佔用情況,以及導出整個JVM中的內存信息。
圖4 JVisualVM Memory Profiler圖示
查看JVM中各個代的內存情況
在linux上執行jmap -heap [pid],就可查看整個JVM中內存的情況,看到的信息相似以下(和JDK版本、GC策略有關):
using thread-local object allocation. Parallel GC with 8 thread(s) Heap Configuration: MinHeapFreeRatio = 40 MaxHeapFreeRatio = 70 MaxHeapSize = 1610612736 (1536.0MB) NewSize = 524288000 (500.0MB) MaxNewSize = 524288000 (500.0MB) OldSize = 4194304 (4.0MB) NewRatio = 8 SurvivorRatio = 8 PermSize = 100663296 (96.0MB) MaxPermSize = 268435456 (256.0MB) Heap Usage: PS Young Generation Eden Space: capacity = 430702592 (410.75MB) used = 324439936 (309.4100341796875MB) free = 106262656 (101.3399658203125MB) 75.32806675098904% used From Space: capacity = 46333952 (44.1875MB) used = 13016424 (12.413429260253906MB) free = 33317528 (31.774070739746094MB) 28.092626331550566% used To Space: capacity = 46792704 (44.625MB) used = 0 (0.0MB) free = 46792704 (44.625MB) 0.0% used PS Old Generation capacity = 1086324736 (1036.0MB) used = 945707880 (901.8973159790039MB) free = 140616856 (134.1026840209961MB) 87.05572548059884% used PS Perm Generation capacity = 100663296 (96.0MB) used = 46349592 (44.202415466308594MB) free = 54313704 (51.797584533691406MB) 46.044182777404785% used |
要注意的是在使用CMS GC的狀況下,jmap -heap的執行有可能會致使Java進程被掛起。
JVM中對象的內存的佔用狀況
在查看JVM內存情況時,除了要知道每一個代的佔用狀況外,不少時候更要知道其中各個對象佔用的內存大小,這樣便於分析對象的內存佔用狀況,在分析OutOfMemory的場景中尤爲適用。
輸入jmap -histo [pid]便可查看jvm堆中對象的詳細佔用狀況,如圖1所示。
圖1 jmap -histo運行效果
輸出內容按照佔用空間的大小排序,例如上面的[C,表示char類型的對象在jvm中總共有243 707個實例,佔用了501 638 784 bytes的空間。
導出整個JVM中的內存信息
經過以上方法能查看到JVM中對象內存的佔用狀況,但不少時候還要知道這個對象究竟是誰建立的。例如上面顯示出來的[C,只知道它佔用了那麼多的空間,但不知道是什麼對象建立出的[C,因而jmap提供了導出整個jvm中的內存信息的支持。基於一些jvm內存的分析工具,例如sun JDK 6中的jhat、Eclipse Memory Analyzer,能夠分析jvm中內存的詳細信息,例如[C是哪些對象建立的。
執行以下命令便可導出整個jvm中的內存信息:
jmap -dump:format=b,file=文件名 [pid] |
5. JHat
JHat是Sun JDK 6及以上版本中自帶的一個用於分析jvm堆dump文件的工具,基於此工具可分析jvm heap中對象的內存佔用情況、引用關係等。
執行以下命令分析jvm堆的dump文件:
jhat -J-Xmx1024M [file] |
執行後等待console中輸出Started HTTP server on port 7000,看到後就能夠經過瀏覽器訪問http://ip:7000了,此頁面默認爲按package分類顯示系統中全部的對象實例。在頁面的最下端有Other Queries導航,其中有顯示jvm中對象實例個數的連接、有顯示jvm中對象大小的連接等,點擊顯示jvm中對象大小的連接,獲得的結果如圖2所示。
圖2 jhat運行效果
點擊圖2中的class [C,能夠看到有哪些對象實例引用了這個對象,或者建立了這個對象,jhat在分析大的堆dump文件時表現很差,速度很慢。
6. JStat
JStat是Sun JDK自帶的一個統計分析JVM運行情況的工具,位於JDK的bin目錄下,除了可用於分析GC的情況外,還可用於分析編譯的情況、class加載的情況等。
JStat用於GC分析的參數有:-gc、-gccapacity、-gccause、-gcnew、-gcnewcapacity、-gcold、-gcoldcapacity、-gcpermcapacity、-gcutil。經常使用的爲-gcutil。經過-gcutil可按必定頻率查看jvm中各代的空間的佔用狀況、minor GC的次數、消耗的時間、full GC的次數及消耗的時間的統計,執行jstat -gcutil [pid] [interval],可看到相似以下的輸出信息:
S0 S1 E O P YGC YGCT FGC FGCT GCT 0.00 74.24 96.73 73.43 46.05 17808 382.335 208 315.197 697.533 45.37 0.00 28.12 74.97 46.05 17809 382.370 208 315.197 697.568 |
其中S0、S1就是Survivor空間的使用率,E表示Eden空間的使用率,O表示舊生代空間的使用率,P表示持久代的使用率,YGC表示minor GC的執行次數,YGCT表示minor GC執行消耗的時間,FGC表示Full GC的執行次數,FGCT表示Full GC執行消耗的時間,GCT表示Minor GC+Full GC執行消耗的時間。
7. Eclipse Memory Analyzer
Eclipse Memory Analyzer是Eclipse提供的一個用於分析jvm堆dump文件的插件,藉助這個插件可查看對象的內存佔用情況、引用關係、分析內存泄露等。
Eclipse Memory Analyzer(MAT)的網站爲:http://www.eclipse.org/mat/,在eclipse中能夠直接遠程安裝此插件。不過因爲此插件在分析堆dump文件時比較耗內存,所以在分析前最好先將eclipse的jvm的內存設置大一點,MAT分析dump文件後的對象佔用內存及引用關係如圖3所示。
相對而言MAT功能比jhat強大不少,分析的速度也快一些,所以,若是要分析jvm堆dumap文件,首選推薦的是MAT。
在進行JVM內存情況分析時,一般要關注的主要有GC的趨勢、內存的具體消耗情況。
GC趨勢對於可圖形界面連到需查看GC情況的機器的狀況而言,VisualVM是經常使用的選擇;對於不能採用圖形界面方式的,輸出GC日誌 及採用jstat命令直接分析是經常使用的選擇。
在查找內存是程序中的什麼對象佔用時,須要分析內存的具體消耗情況,對於有圖形界面可用的狀況,VisualVM是經常使用的選擇;對於不能採用圖形界面方式的,可經過jmap dump生成文件後,再經過MAT進行分析是經常使用的選擇。