Java虛擬機內存調優經驗

轉載自 http://www.cnblogs.com/jackyrong/archive/2010/01/21/1653163.html 尊重別人的勞動成果就是尊重自己

Java內存組成 

 

1) 堆

    運行時數據區域,所有類實例和數組的內存均從此處分配。Java 虛擬機啓動時創建。對象的堆內存由稱爲垃圾回收器 的自動內存管理系統回收。
 堆由兩部分組成:

    其中eden+fromspace+tospace也叫年輕代(young),old space叫舊生代.

    其中還有S1,S0(在JDK的自帶工具輸出中會看到),分別指的是Survivor space,存放每次垃圾回收後存活的對象.

    Old Generation , 主要存放應用程序中生命週期長的存活對象
垃圾回收主要是對Young Generation塊和Old Generation塊內存進行回收,YG用來放新產生的對象,經過幾次回收還沒回收掉的對象往OG中移動,
對YG進行垃圾回收又叫做MinorGC,對OG垃圾回收叫MajorGC,兩塊內存回收互不干涉

 

2) 非堆內存

   JVM具有一個由所有線程共享的方法區。方法區屬於非堆內存。它存儲每個類結構,如運行時常數池、字段和方法數據,以及方法和構造方法的代碼。它是在 Java 虛擬機啓動時創建的。
    除了方法區外,Java 虛擬機實現可能需要用於內部處理或優化的內存,這種內存也是非堆內存。 例如,JIT 編譯器需要內存來存儲從 Java 虛擬機代碼轉換而來的本機代碼,從而獲得高性能。
 
   Permanent Generation   (圖中的Permanent Space) 存放JVM自己的反射對象,比如類對象和方法對象
3) 回收算法和過程
   

 JVM採用一種分代回收 (generational collection) 的策略,用較高的頻率對年輕的對象(young generation)進行掃描和回收,這種叫做minor collection,而對老對象(old generation)的檢查回收頻率要低很多,稱爲major collection。這樣就不需要每次GC都將內存中所有對象都檢查一遍。

      當一個URL被訪問時,內存申請過程 如下:
A. JVM會試圖爲相關Java對象在Eden中初始化一塊內存區域
B. 當Eden空間足夠時,內存申請結束。否則到下一步
C. JVM試圖釋放在Eden中所有不活躍的對象(這屬於1或更高級的垃圾回收), 釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區
D. Survivor區被用來作爲Eden及OLD的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,否則會被保留在Survivor區
E. 當OLD區空間不夠時,JVM會在OLD區進行完全的垃圾收集(0級)
F. 完全垃圾收集後,若Survivor及OLD區仍然無法存放從Eden複製過來的部分對象,導致JVM無法在Eden區爲新對象創建內存區域,則出現”out of memory錯誤”

    對象衰老的過程 
    young generation的內存,由一塊Eden(伊甸園,有意思)和兩塊Survivor Space(1.4文檔中稱爲semi-space)構成。新創建的對象的內存都分配自eden。兩塊Survivor Space總有會一塊是空閒的,用作copying collection的目標空間。Minor collection的過程就是將eden和在用survivor space中的活對象copy到空閒survivor space中。所謂survivor,也就是大部分對象在伊甸園出生後,根本活不過一次GC。對象在young generation裏經歷了一定次數的minor collection後,年紀大了,就會被移到old generation中,稱爲tenuring。(是否僅當survivor space不足的時候纔會將老對象tenuring? 目前資料中沒有找到描述)
     剩餘內存空間不足會觸發GC,如eden空間不夠了就要進行minor collection,old generation空間不夠要進行major collection,permanent generation空間不足會引發full GC。

 

4 接下來這部分講解的是TOMCAT或者其他服務器出現如下錯誤時的分析:

   1、首先是:java.lang.OutOfMemoryError: Java heap space 

解釋: 

Heap size 設置 

JVM堆的設置是指java程序運行過程中JVM可以調配使用的內存空間的設置.JVM在啓動的時候會自動設置Heap size的值,其初始空間(即-Xms)是物理內存的1/64,最大空間(-Xmx)是物理內存的1/4。可以利用JVM提供的-Xmn -Xms -Xmx等選項可進行設置。Heap size 的大小是Young Generation 和Tenured Generaion 之和。 
提示:在JVM中如果98%的時間是用於GC且可用的Heap size 不足2%的時候將拋出此異常信息。 
提示:Heap Size 最大不要超過可用物理內存的80%,一般的要將-Xms和-Xmx選項設置爲相同,而-Xmn爲1/4的-Xmx值。 

解決方法: 

手動設置Heap size 
修改TOMCAT_HOME/bin/catalina.bat,在“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行: 
Java代碼 
set JAVA_OPTS=%JAVA_OPTS% -server -Xms800m -Xmx800m -XX:MaxNewSize=256m   

set JAVA_OPTS=%JAVA_OPTS% -server -Xms800m -Xmx800m -XX:MaxNewSize=256m 

或修改catalina.sh 
在“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行: 
JAVA_OPTS="$JAVA_OPTS -server -Xms800m -Xmx800m -XX:MaxNewSize=256m" 

2、其次是:java.lang.OutOfMemoryError: PermGen space 

原因: 

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)那麼就會產生此錯誤信息了。 

解決方法: 

1. 手動設置MaxPermSize大小 
修改TOMCAT_HOME/bin/catalina.bat(Linux下爲catalina.sh),在Java代碼 
“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行:    
set JAVA_OPTS=%JAVA_OPTS% -server -XX:PermSize=128M -XX:MaxPermSize=512m   

“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行: 
set JAVA_OPTS=%JAVA_OPTS% -server -XX:PermSize=128M -XX:MaxPermSize=512m 

catalina.sh下爲: 
Java代碼 
JAVA_OPTS="$JAVA_OPTS -server -XX:PermSize=128M -XX:MaxPermSize=512m"  

JAVA_OPTS="$JAVA_OPTS -server -XX:PermSize=128M -XX:MaxPermSize=512m"

 

JVM的默認設置

 (heap)(News Generation 和Old Generaion 之和)的設置

       初始分配的內存由-Xms指定,默認是物理內存的1/64但小於1G。

       最大分配的內存由-Xmx指定,默認是物理內存的1/4但小於1G。

       默認空餘堆內存小於40%時,JVM就會增大堆直到-Xmx的最大限制,可以由-XX:MinHeapFreeRatio=指定。 
默認空餘堆內存大於70%時,JVM會減少堆直到-Xms的最小限制,可以由-XX:MaxHeapFreeRatio=指定。

服務器一般設置-Xms、-Xmx相等以避免在每次GC 後調整堆的大小,所以上面的兩個參數沒啥用。 

       -Xmn 設置young generation的heap大小

      -XX:MinHeapFreeRatio與-XX:MaxHeapFreeRatio設定空閒內存佔總內存的比例範圍,這兩個參數會影響GC的頻率和單次GC的耗時。-XX:NewRatio決定young與old generation的比例。Young generation空間越大,minor collection頻率越低,但是old generation空間小了,又可能導致major collection頻率增加。-XX:NewSize和-XX:MaxNewSize直接指定了young generation的缺省大小和最大大小。

 

非堆內存 的設置

     默認分配爲64M

     -XX:PermSize設置最小分配空間,-XX:MaxPermSize設置最大分配空間。一般把這兩個數值設爲相同,以減少申請內存空間的時間。

 

再講解和筆記下,JDK下的一些相關看內存管理工具的使用:

查看jvm內存狀態:
jstat -gcutil pid 1000 20

異常情況的例子

jstat -gcutil pid 1000 20

 S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT  
  0.00   0.00  99.99  82.51  53.11   2409    1.205 10117 7250.393 7251.598
  0.00   0.00  83.42  82.55  53.10   2409    1.205 10118 7252.650 7253.855
  0.00   0.00  56.06  82.46  53.10   2410    1.205 10120 7254.467 7255.672
  0.00   0.00  32.11  82.55  53.10   2411    1.205 10121 7256.673 7257.877
  0.00   0.00  99.99  82.55  53.10   2412    1.205 10123 7257.026 7258.231
  0.00   0.00  76.00  82.50  53.10   2412    1.205 10124 7259.241 7260.446
 
這個數據顯示Full GC頻繁發生。
 正常情況的例子

  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT

  0.00   0.00   0.24  55.39  99.60    171    0.667  1339  393.364  394.031

  0.00   0.00   0.24  55.39  99.60    171    0.667  1339  393.364  394.031

  0.00   0.00   0.24  55.39  99.60    171    0.667  1339  393.364  394.031

  0.00   0.00   0.24  55.39  99.60    171    0.667  1339  393.364  394.031

  0.00   0.00   0.24  55.39  99.60    171    0.667  1339  393.364  394.031

  0.00   0.00   0.24  55.39  99.60    171    0.667  1339  393.364  394.031
 
參數含義:
S0:Heap上的 Survivor space 0 段已使用空間的百分比 
S1:Heap上的 Survivor space 1 段已使用空間的百分比 
E: Heap上的 Eden space 段已使用空間的百分比 
O: Heap上的 Old space 段已使用空間的百分比 
P: Perm space 已使用空間的百分比 
YGC:從程序啓動到採樣時發生Young GC的次數 
YGCT:Young GC所用的時間(單位秒) 
FGC:從程序啓動到採樣時發生Full GC的次數 
FGCT:Full GC所用的時間(單位秒) 
GCT:用於垃圾回收的總時間(單位秒) 
 2         Dump出內存
2.1       找出要dump的線程pid

 
在Linux下,使用ps –aux
 
2.2       Dump出內存使用詳情
可以通過命令:

jmap -dump:file=a.hprof pid

例如:jmap -heap 2343,可以看到

Attaching to process ID 2343, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.0-b16

using thread-local object allocation.
Parallel GC with 8 thread(s)

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 4294967296 (4096.0MB)
   NewSize          = 2686976 (2.5625MB)
   MaxNewSize       = -65536 (-0.0625MB)
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2                   (YG,OG 大小比爲1:2)
   SurvivorRatio    = 8
   PermSize         = 21757952 (20.75MB)
   MaxPermSize      = 268435456 (256.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 1260060672 (1201.6875MB)
   used     = 64868288 (61.86322021484375MB)
   free     = 1195192384 (1139.8242797851562MB)
   5.148028935546367% used
From Space:
   capacity = 85524480 (81.5625MB)
   used     = 59457648 (56.70323181152344MB)
   free     = 26066832 (24.859268188476562MB)
   69.52120375359195% used
To Space:
   capacity = 85852160 (81.875MB)
   used     = 0 (0.0MB)
   free     = 85852160 (81.875MB)
   0.0% used
~~~~~~~~~~~~~~~~~~~~~~~~~~這三塊爲上面所說的YG大小和使用情況
PS Old Generation
   capacity = 2291138560 (2185.0MB)
   used     = 1747845928 (1666.8757705688477MB)
   free     = 543292632 (518.1242294311523MB)
   76.28722062099989% used
~~~~~~~~~~~~~~~~~~~~~~~~~~OG大小和使用情況
PS Perm Generation
   capacity = 108265472 (103.25MB)
   used     = 107650712 (102.6637191772461MB)
   free     = 614760 (0.5862808227539062MB)
   99.43217353728436% used

jstat
jstat是vm的狀態監控工具,監控的內容有類加載、運行時編譯及GC。

使用時,需加上查看進程的進程id,和所選參數。以下詳細介紹各個參數的意義。  
    jstat -class pid:顯示加載class的數量,及所佔空間等信息。  
    jstat -compiler pid:顯示VM實時編譯的數量等信息。  
    jstat -gc pid:可以顯示gc的信息,查看gc的次數,及時間。其中最後五項,分別是young gc的次數,young gc的時間,full gc的次數,full gc的時間,gc的總時間。  
    jstat -gccapacity:可以顯示,VM內存中三代(young,old,perm)對象的使用和佔用大小,如:PGCMN顯示的是最小perm的內存使用量,PGCMX顯示的是perm的內存最大使用量,PGC是當前新生成的perm內存佔用量,PC是但前perm內存佔用量。其他的可以根據這個類推, OC是old內純的佔用量。  
    jstat -gcnew pid:new對象的信息。  
    jstat -gcnewcapacity pid:new對象的信息及其佔用量。  
    jstat -gcold pid:old對象的信息。  
    jstat -gcoldcapacity pid:old對象的信息及其佔用量。  
    jstat -gcpermcapacity pid: perm對象的信息及其佔用量。  
    jstat -util pid:統計gc信息統計。  
    jstat -printcompilation pid:當前VM執行的信息。  
    除了以上一個參數外,還可以同時加上 兩個數字,如:jstat -printcompilation 3024 250 6是每250毫秒打印一次,一共打印6次,還可以加上-h3每三行顯示一下標題。  
例子:

jstat -gcutil pid 1000 20

 S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT   
 47.49   0.00  64.82  46.08  47.69  20822 2058.631    68   22.734 2081.365
  0.00  37.91  38.57  46.13  47.69  20823 2058.691    68   22.734 2081.425  這裏發生了一次YG GC,也就是MinorGC,耗時0.06s
 46.69   0.00  15.19  46.18  47.69  20824 2058.776    68   22.734 2081.510
 46.69   0.00  74.59  46.18  47.69  20824 2058.776    68   22.734 2081.510
  0.00  40.29  19.95  46.24  47.69  20825 2058.848    68   22.734 2081.582

MajorGC平均時間:22.734/68=0.334秒

MinorGC平均時間:2058.691/20823=0.099秒

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