前面在學習JVM的知識的時候,一般都需要利用相關參數進行分析,而分析一般都需要用到一些分析的工具,因為一般使用IDEA,而VisualVM對於IDEA也不錯,所以就選擇VisualVM來分析JVM性能,這篇文章就介紹一下
如何利用VisualVM進行性能分析,以及在分析之前需要知道一些GC優化的原則,GC優化的目的,以及遇到問題時怎麼去解決問題的方法。1 為什麼需要
開發大型 Java 應用程序的過程中難免遇到內存洩露、性能瓶頸等問題,比如文件、網絡、數據庫的連接未釋放,未優化的算法等。隨著應用程序的持續運行,可能會造成整個系統運行效率下降,嚴重的則會造成系統崩潰。為了找出程序中隱藏的這些問題,在項目開發後期往往會使用性能分析工具來對應用程序的性能進行分析和優化。
VisualVM 是一款免費的性能分析工具。它通過 jvmstat、JMX、SA(Serviceability Agent)以及 Attach API 等多種方式從程序運行時獲得實時數據,從而進行動態的性能分析。同時,它能自動選擇更快更輕量級的技術儘量減少性能分析對應用程序造成的影響,提高性能分析的精度。
2 如何安裝
這裡有兩種方式:
- 沒有按照IDEA插件
如果沒有按照IDEA插件的話,我們需要找到JDK的按照目錄bin下找到如下執行程序。
然後雙擊執行,就會出現界面,如下;
但是,我們一般使用IDEA,所以會使用插件,就是下面這種方式。
- 按照IDEA插件
先在插件中找到VisualVM安裝;
安裝了之後,在運行的地方就會多出現兩個VisualVM的運行按鈕;
這樣運行程序之後,就可以自動打開VisualVM程序了。
3 基本介紹
這一部分先對這個工具做一個簡要的介紹,看看基本有哪些我們會用到的功能。
在沒有添加其他插件的時候,是隻有下面幾個功能的。
3.1 概述
如上圖所示,概述基本上都是我們的系統屬性、運行程序時設置的JVM參數等信息的展示,所以,這一部分可以讓我們查看這些信息。
3.2 監視
監視這個界面的功能還是很有作用的,可以看到cup運行情況、堆的使用情況、類的情況以及線程的動態情況。
因此,我們可以利用這個界面查看cpu情況好不好,更重要的是,我們可以查看堆的使用情況,這對於我們分析JVM還是非常重要的。
3.3 線程
如上圖所以,可以看到所有的線程的情況,是運行、休眠、等待、駐留、監視等情況。
注意, 以上這些都不是關鍵,關鍵是VisualVM中還有一個很重要的功能,可以添加插件獲取更多的功能。
3.4 插件添加
正是因為有了插件的擴展功能,所以這個工具才如此強大,VisualVM可以做到以下:
- 顯示虛擬機進程以及進程的配置、環境信息、jps、jinfo。
- 監視應用程序的cpu、GC、堆、方法區以及線程的信息(jstat、jstack)。
- dump以及分析堆轉存儲快照(jmap、jhat)。
- 還有很多其他的功能。
在工具->找到可用插件,安裝即可。
下一部分我們就利用已經安裝的插件Visual GC進行分析。
4 利用Visual GC分析虛擬機內存區域
這部分會用到一些Java虛擬機的一些基礎知識,所以,查看這部分之前,請先查看這篇文章:。
在這個界面分為以下幾個部分。
- space(Metaspace(元數據)、Old老年代、新生代(Eden、S0、S1))
- Graphs(Compile Time(編譯時間)、Class Loader Time(類加載時間)、GC Time(垃圾收集時間)、Eden Space、Survivor 0、Survivor 1、Old Gen、Metaspace)
- Histogram(Parameters參數設置)
那麼知道這些參數之後,怎麼去分析虛擬機到底運行是好是壞呢,這個時候,我們需要了解一些Java虛擬機基礎的優化知識。
首先,需要了解一些GC優化的原則。
- 多數的Java應用不需要在服務器上進行GC優化;
- 多數導致GC問題的Java應用,都不是因為我們參數設置錯誤,而是代碼問題;
- 在應用上線之前,先考慮將機器的JVM參數設置到最優(最適合);
- 減少創建對象的數量;
- 減少使用全局變量和大對象;
- GC優化是到最後不得已才採用的手段;
- 在實際使用中,分析GC情況優化代碼比優化GC參數要多得多;
另外,我們需要知道我們GC優化的目的。
- 將轉移到老年代的對象數量降低到最小;
- 減少full GC的執行時間;
一般,我們需要執行的有以下幾點;
- 減少使用全局變量和大對象;
- 調整新生代的大小到最合適;
- 設置老年代的大小為最合適;
- 選擇合適的GC收集器;
至於怎麼算合適,後面我會通過一個實例講解。
一般我們執行了我們的程序之後,接下來就是需要查看GC的狀態了,接著分析結果,判斷是否需要進行優化。
一般如果達到以下的指標,就不需要進行GC了。
- Minor GC執行時間不到50ms,Minor GC執行不頻繁,約10秒一次;
- Full GC執行時間不到1s,Full GC執行頻率不算頻繁,不低於10分鐘1次;
實例 1
我們先看一個GC狀態需要優化的例子,在這個實例中,我們給堆分配的最大最小的值都是64M(很小的堆大小)。
GC狀態差情況分析
<code>/**
* VM Args:-Xms64m -Xmx64m -XX:+HeapDumpOnOutOfMemoryError
* @author 歐陽思海
*/
public class HeapTest {
static class StaticObject {
}
public static void main(String[] args) {
List<staticobject> list = new ArrayList<staticobject>();
int i = 1;
//不斷的向堆中添加對象
while (true) {
list.add(new StaticObject());
i++;
System.out.println(i);
System.out.println(list.size());
}
}
}
/<staticobject>/<staticobject>/<code>
由於分配的堆內存太小,所以導致,堆溢出。
接著我們查看一下Visual GC的監視情況。
- 監視界面情況
我們可以從堆的使用情況看出,基本已經使用完。
- Visual GC監視情況
從上圖可知,在短短的運行時間中,Eden進行了49次GC,雖然時間短,但是能說明一個問題,新生代堆內存分配的空間太小,導致頻繁GC。
同時,Old老年代也進行了33次GC,雖然運行時間也在不需要優化的範圍內,而且從Survivor可以看出,基本沒有GC,說明這些都是大對象,直接進入到了Old老年代,導致GC頻繁 。
所以,我們需要進行的優化就是加大新生代和老年代堆內存的大小,同時減少大對象的產生。
參數優化分析
我們將VM參數改為:-Xms512m -Xmx512m -Xmn128m -XX:+HeapDumpOnOutOfMemoryError,運行大概5分鐘再次查看結果。
首先沒有出現堆內存溢出。
- 監視情況
加大了堆內存,所以堆內存沒有出現問題。
- Visual GC監視情況
加大了堆內存之後,Eden新生代進行了66次GC,使用時間3.381s基本滿足要求(執行時間不到50ms,Minor GC執行不頻繁,約10秒一次),同時老年代old進行了2次GC,使用時間4.127s,這裡還是有待優化的,不太滿足優化要求。
- dump文件分析
點擊堆 dump這個按鈕就會生成 dump文件,我們可以分析類及對象的一些情況。
分析之後發現,StaticObject對象大多,沒有進行GC,問題主要在這裡,所以,下一步需要解決這個問題。
通過以上分析可以說明一個問題,加大了堆內存之後,新生代和老年代的GC情況大大的改善了,但是還有大對象的問題,所以還有待優化。
修改大對象,進行GC
修改程序,如下:
<code>/**
* VM Args:-Xms512m -Xmx512m -Xmn128m -XX:+HeapDumpOnOutOfMemoryError
*
* @author 歐陽思海
*/
public class HeapTest {
/* static class StaticObject {
}*/
public static void main(String[] args) {
int i = 1;
while (true) {
i++;
System.out.println(i);
}
}
}
/<code>
- 監視情況
堆一直運行良好。
- Visual GC監視情況
這次相對於上次相比,老年代的情況已經改善了,沒有GC,說明大對象不存在了。
通過上面的分析跟優化,就滿足GC的需求了,不需要再優化了。
5 總結
通過上面的分析及使用,VisualVM基本的使用以及如何利用VisualVM進行Java虛擬機優化相信你已經掌握了。
原出處:https://my.oschina.net/u/4116286/blog/3150920
閱讀更多 Java架構學習交流 的文章