阿里學習專題-性能調優專題三(JVM調優)

淺談JVM調優

1.JVM組成結構是什麼樣的?

JVM的內存結構如下圖所示

2.什麼是JVM運行時數據區?

 Java虛擬機在執行Java程序的過程中會把它管理的內存分爲若干個不同的數據區域。這些區域有着各自的用途,一級創建和銷燬的時間,有的區域隨着虛擬機進程的啓動而存在,有些區域則依賴用戶線程的啓動和結束而建立和銷燬。根據《Java虛擬機規範》中規定,jvm所管理的內存大致包括以下幾個運行時數據區域

1.程序計數器

  程序計數器是很小的一塊內存區域,可以看做是當前線程所執行字節碼的行號指示器。在虛擬機的概念模型中,字節碼解釋器工作時就是通過改變程序計數器的值來選取下一條需要執行的字節碼指令,分支,循環,跳轉,異常處理,線程恢復等基礎功能均依賴於程序計數器。在多線程中,每個線程都有一個獨立的程序計數器,每個線程的程序計數器之間互不影響,即“線程私有”。同時,程序計數器是java虛擬機規範中唯一一個沒有規定OutOfMemoryError的區域。

2.java虛擬機棧

  虛擬機棧描述的是java方法執行的內存模型,每個方法在執行時候都會創建一個棧幀用於存儲局部變量表,操作數棧,動態鏈接,方法出口等信息。每個方法從被調用執行到執行完成的過程,就對應着一個棧幀在虛擬機棧中從入棧到出棧的過程。虛擬機棧也是線程私有的。在Java虛擬機規範中,虛擬機棧有兩種異常狀況:

(1)線程請求的棧深度大於虛擬機棧所允許的深度,將拋出StackOverflowError異常;

(2)如果虛擬機棧可以動態擴展,但擴展時無法申請到足夠的內存,或者在創建新線程時候沒有足夠的內存去創建虛擬機棧,就會拋出OutOfMemoryError異常。

3.本地方法棧

  本地方法棧和虛擬機棧的作用類似,區別在於java虛擬機棧支持Java方法執行,而本地方法棧則支持native方法執行。有些虛擬機直接將java虛擬機棧和本地方法棧合二爲一,本地方法棧的異常情況與java虛擬機棧的內存一致,即:

(1)線程請求的棧深度大於本地方法棧所允許的深度,將拋出StackOverflowError異常;

(2)如果本地方法棧可以動態擴展,但擴展時無法申請到足夠的內存,或者在創建新線程時候沒有足夠的內存去創建本地方法棧,就會拋出OutOfMemoryError異常。

4.java堆

  java堆是Java虛擬機所管理的內存中最大的一塊,它是被所有線程所共享的內存區域,在虛擬機啓動時候創建。java堆是供所有類實例和數組分配內存的區域,也是垃圾收集器管理的主要區域,因此很多時候也被稱爲“GC堆”。從內存回收的角度來看,由於收集器基本都採用分代收集算法,所以Java堆還可以細分爲新生代(Young Gen)和老年代(Old Gen)。新生代又可以繼續分爲Eden空間,From Survivor空間,To Survivor空間

 Java堆的大小可以是固定的,也可以是隨着程序執行動態擴展,並在不需要過多空間時候自動收縮。且Java堆所使用的內存不需要保證是連續的。如果實際所需的堆超過了自動內存管理系統所能提供的最大容量,java虛擬機將會拋出一個OutOfMemoryError異常。

  Java堆常用調節參數:

   -Xms128M :設置初始堆大小爲128M
   -Xmx512M :設置最大堆大小爲512M
   -XX:NewSize=n :設置年輕代大小;
   -XX:NewRatio=n :設置年輕代和年老代的比值,如設置爲3,表示年輕代與年老代比值爲1:3,年輕代佔整個年輕代年老代的1/4;
   -XX:SurvivorRatio=n :年輕代中Eden區與兩個Survivor區的比值(年輕代分成1個Eden Space和2個Suvivor Space),如設置爲3,表示Eden:Survivor=3:2,一個Survivor區佔整個年輕代的1/5;

5.方法區

  方法區和java堆一樣,是所有線程共享的內存區域。它存儲了每一個類的結構信息,例如運行時常量池,字段和方法數據,構造函數和普通方法的字節碼內容,還包括一些在類,實例,接口初始化時用到的特殊方法。由於HotSpot虛擬機設計團隊將GC分代擴展到了方法區,或者說使用“永久代”來實現方法區,這樣HotSpot的垃圾收集器就可以像管理Java堆那樣來管理方法區內存,故很多人將方法區稱爲永久代,但本質上兩者不是等價的(Java8中,HotSpot對於方法區的實現從永久代變更爲元空間Metaspace)對於其他虛擬機(如BEA JRockit,IBM J9等)而言是不存在永久代的。按照java虛擬機規範,當方法區不能滿足內存分配請求時,java虛擬機將拋出OutOfMemoryError異常。

  方法區常用調節參數:

  java8之前(設置永久代):

   -XX:PermSize=64M  :設置持久代初始大小
   -XX:MaxPermSize=128M  :設置持久代最大允許分配大小。

  java8之後(設置元空間):  

   -XX:MetaspaceSize=8M :設置元空間大小爲8M
   -XX:MaxMetaspaceSize=80M :設志元空間最大爲80M
   -XX:MinMetaspaceFreeRatio=n :當進行過Metaspace GC之後,會計算當前Metaspace的空閒空間比,如果空閒比小於這個參數,那麼虛擬機將增長Metaspace的大小。默認值爲40,也就是40%
   -XX:MaxMetasaceFreeRatio=n :當進行過Metaspace GC之後, 會計算當前Metaspace的空閒空間比,如果空閒比大於這個參數,那麼虛擬機會釋放Metaspace的部分空間。默認值爲70,也就是70%
   -XX:MaxMetaspaceExpansion=n :設置Metaspace增長時的最大幅度
   -XX:MinMetaspaceExpansion=n :設置Metaspace增長時的最小幅度

6.運行時常量池

  運行時常量池是方法區的一部分,是class文件中每一個類或者接口的常量池表在運行時的表現形式。在加載類和接口到虛擬機後,就創建對應的運行時常量池。如果構造運行時常量池所需要的內存空間超過了方法區所能提供的最大值,Java虛擬機就會拋出一個OutOfMemoryError異常。

  需要特別注意的是,運行時常量池和字符串常量池的區別,在jdk1.7之前,運行時常量池邏輯包含字符串常量池存放在方法區,此時hotspot虛擬機對方法區的實現爲永久代。在JDK1.7 字符串常量池被從方法區拿到了堆中,運行時常量池剩下的東西還在方法區, 也就是hotspot中的永久代。JDK1.8 hotspot移除了永久代引入元空間(Metaspace), 這時候字符串常量池還在堆中, 運行時常量池還在方法區, 只不過方法區的實現從永久代變成了元空間(Metaspace)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章