使用 VisualVM 進(jìn)行性能分析及調(diào)優(yōu)
概述
開發(fā)大型 Java 應(yīng)用程序的過程中難免遇到內(nèi)存泄露、性能瓶頸等問題,比如文件、網(wǎng)絡(luò)、數(shù)據(jù)庫的連接未釋放,未優(yōu)化的算法等。隨著應(yīng)用程序的持續(xù)運(yùn)行,可能會造成整個系統(tǒng)運(yùn)行效率下降,嚴(yán)重的則會造成系統(tǒng)崩潰。為了找出程序中隱藏的這些問題,在項目開發(fā)后期往往會使用性能分析工具來對應(yīng)用程序的性能進(jìn)行分析和優(yōu)化。
VisualVM 是一款免費(fèi)的性能分析工具。它通過 jvmstat、JMX、SA(Serviceability Agent)以及 Attach API 等多種方式從程序運(yùn)行時獲得實時數(shù)據(jù),從而進(jìn)行動態(tài)的性能分析。同時,它能自動選擇更快更輕量級的技術(shù)盡量減少性能分析對應(yīng)用程序造成的影響,提高性能分析的精度。
本文將對 VisualVM 的主要功能逐一介紹并探討如何利用獲得的數(shù)據(jù)進(jìn)行性能分析及調(diào)優(yōu)。
背景知識
性能分析的主要方式
- 監(jiān)視:監(jiān)視是一種用來查看應(yīng)用程序運(yùn)行時行為的一般方法。通常會有多個視圖(View)分別實時地顯示 CPU 使用情況、內(nèi)存使用情況、線程狀態(tài)以及其他一些有用的信息,以便用戶能很快地發(fā)現(xiàn)問題的關(guān)鍵所在。
- 轉(zhuǎn)儲:性能分析工具從內(nèi)存中獲得當(dāng)前狀態(tài)數(shù)據(jù)并存儲到文件用于靜態(tài)的性能分析。Java 程序是通過在啟動 Java 程序時添加適當(dāng)?shù)臈l件參數(shù)來觸發(fā)轉(zhuǎn)儲操作的。它包括以下三種:
- 系統(tǒng)轉(zhuǎn)儲:JVM 生成的本地系統(tǒng)的轉(zhuǎn)儲,又稱作核心轉(zhuǎn)儲。一般的,系統(tǒng)轉(zhuǎn)儲數(shù)據(jù)量大,需要平臺相關(guān)的工具去分析,如 Windows 上的 windbg 和 Linux 上的 gdb。
- Java 轉(zhuǎn)儲:JVM 內(nèi)部生成的格式化后的數(shù)據(jù),包括線程信息,類的加載信息以及堆的統(tǒng)計數(shù)據(jù)。通常也用于檢測死鎖。
- 堆轉(zhuǎn)儲:JVM 將所有對象的堆內(nèi)容存儲到文件。
- 快照:應(yīng)用程序啟動后,性能分析工具開始收集各種運(yùn)行時數(shù)據(jù),其中一些數(shù)據(jù)直接顯示在監(jiān)視視圖中,而另外大部分?jǐn)?shù)據(jù)被保存在內(nèi)部,直到用戶要求獲取快照,基于這些保存的數(shù)據(jù)的統(tǒng)計信息才被顯示出來。快照包含了應(yīng)用程序在一段時間內(nèi)的執(zhí)行信息,通常有 CPU 快照和內(nèi)存快照兩種類型。
- CPU 快照:主要包含了應(yīng)用程序中函數(shù)的調(diào)用關(guān)系及運(yùn)行時間,這些信息通??梢栽?CPU 快照視圖中進(jìn)行查看。
- 內(nèi)存快照:主要包含了內(nèi)存的分配和使用情況、載入的所有類、存在的對象信息及對象間的引用關(guān)系等。這些信息通??梢栽趦?nèi)存快照視圖中進(jìn)行查看。
- 性能分析:性能分析是通過收集程序運(yùn)行時的執(zhí)行數(shù)據(jù)來幫助開發(fā)人員定位程序需要被優(yōu)化的部分,從而提高程序的運(yùn)行速度或是內(nèi)存使用效率,主要有以下三個方面:
- CPU 性能分析:CPU 性能分析的主要目的是統(tǒng)計函數(shù)的調(diào)用情況及執(zhí)行時間,或者更簡單的情況就是統(tǒng)計應(yīng)用程序的 CPU 使用情況。通常有 CPU 監(jiān)視和 CPU 快照兩種方式來顯示 CPU 性能分析結(jié)果。
- 內(nèi)存性能分析:內(nèi)存性能分析的主要目的是通過統(tǒng)計內(nèi)存使用情況檢測可能存在的內(nèi)存泄露問題及確定優(yōu)化內(nèi)存使用的方向。通常有內(nèi)存監(jiān)視和內(nèi)存快照兩種方式來顯示內(nèi)存性能分析結(jié)果。
- 線程性能分析:線程性能分析主要用于在多線程應(yīng)用程序中確定內(nèi)存的問題所在。一般包括線程的狀態(tài)變化情況,死鎖情況和某個線程在線程生命期內(nèi)狀態(tài)的分布情況等
VisualVM 安裝
VisualVM 是一個性能分析工具,自從 JDK 6 Update 7 以后已經(jīng)作為 Oracle JDK 的一部分,位于 JDK 根目錄的 bin 文件夾下。VisualVM 自身要在 JDK6 以上的版本上運(yùn)行,但是它能夠監(jiān)控 JDK1.4 以上版本的應(yīng)用程序。下面主要介紹如何安裝 VisualVM 以及各種 VisualVM 上的插件。
安裝 VisualVM
VisualVM 項目的官方網(wǎng)站目前提供英文版本和多語言支持版本下載。多語言版本主要支持英語、日語以及中文三種語言。如果下載安裝多語言版本的 VisualVM,安裝程序會依據(jù)操作系統(tǒng)的當(dāng)前語言環(huán)境去安裝相應(yīng) VisualVM 的語言版本。最新 VisualVM 版本主要支持的操作系統(tǒng)包括:Microsoft Windows (7, Vista, XP, Server)、Linux、Sun Solaris、Mac OS X、HP-UX 11i。本文以 Microsoft Windows XP 為安裝環(huán)境并支持中文。
- 從 VisualVM 項目的官方網(wǎng)站上下載 VisualVM 安裝程序。
- 將 VisualVM 安裝程序解壓縮到本地系統(tǒng)。
- 導(dǎo)航至 VisualVM 安裝目錄的 bin 目錄,然后啟動 jvisualvm.exe。
安裝 VisualVM 上的插件
VisualVM 插件中心提供很多插件以供安裝向 VisualVM 添加功能??梢酝ㄟ^ VisualVM 應(yīng)用程序安裝,或者從 VisualVM 插件中心手動下載插件,然后離線安裝。另外,用戶還可以通過下載插件分發(fā)文件 (.nbm 文件 ) 安裝第三方插件為 VisualVM 添加功能。
從 VisualVM 插件中心安裝插件安裝步驟 :
- 從主菜單中選擇“工具”>“插件”。
- 在“可用插件”標(biāo)簽中,選中該插件的“安裝”復(fù)選框。單擊“安裝”。
- 逐步完成插件安裝程序。
圖 1. VisualVM 插件管理器

根據(jù) .nbm 文件安裝第三方插件安裝步驟 :
- 從主菜單中選擇“工具”>“插件”。
- 在“已下載”標(biāo)簽中,點擊"添加插件"按鈕,選擇已下載的插件分發(fā)文件 (.nbm) 并打開。
-
選中打開的插件分發(fā)文件,并單擊"安裝"按鈕,逐步完成插件安裝程序。
圖 2. 通過 .nbm 文件安裝 VisualVM 插件
圖 2.jpg
功能介紹
下面我們將介紹性能分析的幾種常見方式以及如何使用 VisualVM 性能分析工具進(jìn)行分析。
內(nèi)存分析
VisualVM 通過檢測 JVM 中加載的類和對象信息等幫助
我們分析內(nèi)存使用情況,我們可以通過 VisualVM 的監(jiān)視標(biāo)簽和 Profiler 標(biāo)簽對應(yīng)用程序進(jìn)行內(nèi)存分析。
在監(jiān)視標(biāo)簽內(nèi),我們可以看到實時的應(yīng)用程序內(nèi)存堆以及永久保留區(qū)域的使用情況。
圖 3. 內(nèi)存堆使用情況

圖 4. 永久保留區(qū)域使用情況

此外,我們也可以通過 Applications 窗口右擊應(yīng)用程序節(jié)點來啟用“在出現(xiàn) OOME 時生成堆 Dump”功能,當(dāng)應(yīng)用程序出現(xiàn) OutOfMemory 例外時,VisualVM 將自動生成一個堆轉(zhuǎn)儲。
圖 5. 開啟“在出現(xiàn) OOME 時生成堆”功能

在 Profiler 標(biāo)簽,點擊“內(nèi)存”按鈕將啟動一個內(nèi)存分析會話,等 VisualVM 收集和統(tǒng)計完相關(guān)性能數(shù)據(jù)信息,將會顯示在性能分析結(jié)果。通過內(nèi)存性能分析結(jié)果,我們可以查看哪些對象占用了較多的內(nèi)存,存活的時間比較長等,以便做進(jìn)一步的優(yōu)化。
此外,我們可以通過性能分析結(jié)果下方的類名過濾器對分析結(jié)果進(jìn)行過濾。
圖 6. 內(nèi)存分析結(jié)果

CPU 分析
VisualVM 能夠監(jiān)控應(yīng)用程序在一段時間的 CPU 的使用情況,顯示 CPU 的使用率、方法的執(zhí)行效率和頻率等相關(guān)數(shù)據(jù)幫助我們發(fā)現(xiàn)應(yīng)用程序的性能瓶頸。我們可以通過 VisualVM 的監(jiān)視標(biāo)簽和 Profiler 標(biāo)簽對應(yīng)用程序進(jìn)行 CPU 性能分析。
在監(jiān)視標(biāo)簽內(nèi),我們可以查看 CPU 的使用率以及垃圾回收活動對性能的影響。過高的 CPU 使用率可能是由于我們的項目中存在低效的代碼,可以通過 Profiler 標(biāo)簽的 CPU 性能分析功能進(jìn)行詳細(xì)的分析。如果垃圾回收活動過于頻繁,占用了較高的 CPU 資源,可能是由內(nèi)存不足或者是新生代和舊生代分配不合理導(dǎo)致的等。
圖 7. CPU 使用情況

在 Profiler 標(biāo)簽,點擊“CPU”按鈕啟動一個 CPU 性能分析會話 ,VisualVM 會檢測應(yīng)用程序所有的被調(diào)用的方法。當(dāng)進(jìn)入一個方法時,線程會發(fā)出一個“method entry”的事件,當(dāng)退出方法時同樣會發(fā)出一個“method exit”的事件,這些事件都包含了時間戳。然后 VisualVM 會把每個被調(diào)用方法的總的執(zhí)行時間和調(diào)用的次數(shù)按照運(yùn)行時長展示出來。
此外,我們也可以通過性能分析結(jié)果下方的方法名過濾器對分析結(jié)果進(jìn)行過濾。
圖 8. CPU 性能分析結(jié)果

線程分析
Java 語言能夠很好的實現(xiàn)多線程應(yīng)用程序。當(dāng)我們對一個多線程應(yīng)用程序進(jìn)行調(diào)試或者開發(fā)后期做性能調(diào)優(yōu)的時候,往往需要了解當(dāng)前程序中所有線程的運(yùn)行狀態(tài),是否有死鎖、熱鎖等情況的發(fā)生,從而分析系統(tǒng)可能存在的問題。
在 VisualVM 的監(jiān)視標(biāo)簽內(nèi),我們可以查看當(dāng)前應(yīng)用程序中所有活動線程和守護(hù)線程的數(shù)量等實時信息。
圖 9. 活躍線程情況

VisualVM 的線程標(biāo)簽提供了三種視圖,默認(rèn)會以時間線的方式展現(xiàn)。另外兩種視圖分別是表視圖和詳細(xì)信息視圖。
時間線視圖上方的工具欄提供了縮小,放大和自適應(yīng)三個按鈕,以及一個下拉框,我們可以選擇將所有線程、活動線程或者完成的線程顯示在視圖中。
圖 10. 線程時間線視圖

圖 11. 線程表視圖

我們在詳細(xì)信息視圖中不但可以查看所有線程、活動線程和結(jié)束的線程的詳細(xì)數(shù)據(jù),而且也可以查看某個線程的詳細(xì)情況。
圖 12. 線程詳細(xì)視圖

快照功能
我們可以使用 VisualVM 的快照功能生成任意個性能分析快照并保存到本地來輔助我們進(jìn)行性能分析??煺諡椴东@應(yīng)用程序性能分析數(shù)據(jù)提供了一個很便捷的方式因為快照一旦生成可以在任何時候離線打開和查看,也可以相互傳閱。
VisualVM 提供了兩種類型的快照:
-
Profiler 快照:當(dāng)有一個性能分析會話(內(nèi)存或者 CPU)正在進(jìn)行時,我們可以通過性能分析結(jié)果工具欄的“快照”按鈕生成 Profiler 快照捕獲當(dāng)時的性能分析數(shù)據(jù)。
圖 13. Profiler 快照
圖 13.jpg -
應(yīng)用程序快照:我們可以右鍵點擊左側(cè) Applications 窗口中應(yīng)用程序節(jié)點,選擇“應(yīng)用程序快照”為生成一個應(yīng)用程序快照。應(yīng)用程序快照會收集某一時刻的堆轉(zhuǎn)儲,線程轉(zhuǎn)儲和 Profiler 快照,同時也會捕獲 JVM 的一些基本信息。
圖 14. 應(yīng)用程序快照
圖 14.gif
轉(zhuǎn)儲功能
線程轉(zhuǎn)儲的生成與分析
VisualVM 能夠?qū)φ谶\(yùn)行的本地應(yīng)用程序生成線程轉(zhuǎn)儲,把活動線程的堆棧蹤跡打印出來,幫助我們有效了解線程運(yùn)行的情況,診斷死鎖、應(yīng)用程序癱瘓等問題。
圖 15. 線程標(biāo)簽及線程轉(zhuǎn)儲功能

當(dāng) VisualVM 統(tǒng)計完應(yīng)用程序內(nèi)線程的相關(guān)數(shù)據(jù),會把這些信息顯示新的線程轉(zhuǎn)儲標(biāo)簽。
圖 16. 線程轉(zhuǎn)儲結(jié)果

堆轉(zhuǎn)儲的生成與分析
VisualVM 能夠生成堆轉(zhuǎn)儲,統(tǒng)計某一特定時刻 JVM 中的對象信息,幫助我們分析對象的引用關(guān)系、是否有內(nèi)存泄漏情況的發(fā)生等。
圖 17. 監(jiān)視標(biāo)簽及堆轉(zhuǎn)儲功能

當(dāng) VisualVM 統(tǒng)計完堆內(nèi)對象數(shù)據(jù)后,會把堆轉(zhuǎn)儲信息顯示在新的堆轉(zhuǎn)儲標(biāo)簽內(nèi),我們可以看到摘要、類、實例數(shù)等信息以及通過 OQL 控制臺執(zhí)行查詢語句功能。
堆轉(zhuǎn)儲的摘要包括轉(zhuǎn)儲的文件大小、路徑等基本信息,運(yùn)行的系統(tǒng)環(huán)境信息,也可以顯示所有的線程信息。
圖 18. 堆轉(zhuǎn)儲的摘要視圖

從類視圖可以獲得各個類的實例數(shù)和占用堆大小數(shù),分析出內(nèi)存空間的使用情況,找出內(nèi)存的瓶頸,避免內(nèi)存的過度使用。
圖 19. 堆轉(zhuǎn)儲的類視圖

通過實例數(shù)視圖可以獲得每個實例內(nèi)部各成員變量的值以及該實例被引用的位置。首先需要在類視圖選擇需要查看實例的類。
圖 20. 選擇查詢實例數(shù)的類

圖 21. 實例數(shù)視圖

此外,還能對兩個堆轉(zhuǎn)儲文件進(jìn)行比較。通過比較我們能夠分析出兩個時間點哪些對象被大量創(chuàng)建或銷毀。
圖 22. 堆轉(zhuǎn)儲的比較

圖 23. 堆轉(zhuǎn)儲的比較結(jié)果

線程轉(zhuǎn)儲和堆轉(zhuǎn)儲均可以另存成文件,以便進(jìn)行離線分析。
圖 24. 轉(zhuǎn)儲文件的導(dǎo)出

總結(jié)
本文首先簡要列舉了一些性能分析相關(guān)的背景知識。然后介紹了 VisualVM 的下載和安裝。最后從內(nèi)存性能、CPU 性能、快照功能以及轉(zhuǎn)儲功能四個方面展開,進(jìn)一步說明了如何使用 VisualVM 進(jìn)行性能分析。通過本文的介紹,相信讀者對性能分析會有一定的了解,并可以利用 VisualVM 進(jìn)行性能分析。


