如何正確學會Java虛擬機調(diào)優(yōu)?
一點小經(jīng)驗僅供參考:
1) 堆
運行時數(shù)據(jù)區(qū)域,所有類實例和數(shù)組的內(nèi)存均從此處分配。Java 虛擬機啟動時創(chuàng)建。對象的堆內(nèi)存由稱為垃圾回收器 的自動內(nèi)存管理系統(tǒng)回收。
堆由兩部分組成:
其中eden+fromspace+tospace也叫年輕代(young),old space叫舊生代.
其中還有S1,S0(在JDK的自帶工具輸出中會看到),分別指的是Survivor space,存放每次垃圾回收后存活的對象.
Old Generation , 主要存放應用程序中生命周期長的存活對象
垃圾回收主要是對Young Generation塊和Old Generation塊內(nèi)存進行回收,YG用來放新產(chǎn)生的對象,經(jīng)過幾次回收還沒回收掉的對象往OG中移動,
對YG進行垃圾回收又叫做MinorGC,對OG垃圾回收叫MajorGC,兩塊內(nèi)存回收互不干涉
2) 非堆內(nèi)存
JVM具有一個由所有線程共享的方法區(qū)。方法區(qū)屬于非堆內(nèi)存。它存儲每個類結構,如運行時常數(shù)池、字段和方法數(shù)據(jù),以及方法和構造方法的代碼。它是在 Java 虛擬機啟動時創(chuàng)建的。
除了方法區(qū)外,Java 虛擬機實現(xiàn)可能需要用于內(nèi)部處理或優(yōu)化的內(nèi)存,這種內(nèi)存也是非堆內(nèi)存。 例如,JIT 編譯器需要內(nèi)存來存儲從 Java 虛擬機代碼轉(zhuǎn)換而來的本機代碼,從而獲得高性能。
Permanent Generation (圖中的Permanent Space) 存放JVM自己的反射對象,比如類對象和方法對象
3) 回收算法和過程
JVM采用一種分代回收 (generational collection) 的策略,用較高的頻率對年輕的對象(young generation)進行掃描和回收,這種叫做minor collection,而對老對象(old generation)的檢查回收頻率要低很多,稱為major collection。這樣就不需要每次GC都將內(nèi)存中所有對象都檢查一遍。
當一個URL被訪問時,內(nèi)存申請過程 如下:
A. JVM會試圖為相關Java對象在Eden中初始化一塊內(nèi)存區(qū)域
B. 當Eden空間足夠時,內(nèi)存申請結束。否則到下一步
C. JVM試圖釋放在Eden中所有不活躍的對象(這屬于1或更高級的垃圾回收), 釋放后若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區(qū)
D. Survivor區(qū)被用來作為Eden及OLD的中間交換區(qū)域,當OLD區(qū)空間足夠時,Survivor區(qū)的對象會被移到Old區(qū),否則會被保留在Survivor區(qū)
E. 當OLD區(qū)空間不夠時,JVM會在OLD區(qū)進行完全的垃圾收集(0級)
F. 完全垃圾收集后,若Survivor及OLD區(qū)仍然無法存放從Eden復制過來的部分對象,導致JVM無法在Eden區(qū)為新對象創(chuàng)建內(nèi)存區(qū)域,則出現(xiàn)”out of memory錯誤”
對象衰老的過程
young generation的內(nèi)存,由一塊Eden(伊甸園,有意思)和兩塊Survivor Space(1.4文檔中稱為semi-space)構成。新創(chuàng)建的對象的內(nèi)存都分配自eden。兩塊Survivor Space總有會一塊是空閑的,用作copying collection的目標空間。Minor collection的過程就是將eden和在用survivor space中的活對象copy到空閑survivor space中。所謂survivor,也就是大部分對象在伊甸園出生后,根本活不過一次GC。對象在young generation里經(jīng)歷了一定次數(shù)的minor collection后,年紀大了,就會被移到old generation中,稱為tenuring。(是否僅當survivor space不足的時候才會將老對象tenuring? 目前資料中沒有找到描述)
剩余內(nèi)存空間不足會觸發(fā)GC,如eden空間不夠了就要進行minor collection,old generation空間不夠要進行major collection,permanent generation空間不足會引發(fā)full GC。
4 接下來這部分講解的是TOMCAT或者其他服務器出現(xiàn)如下錯誤時的分析:
1、首先是:java.lang.OutOfMemoryError: Java heap space
解釋:
Heap size 設置
JVM堆的設置是指java程序運行過程中JVM可以調(diào)配使用的內(nèi)存空間的設置.JVM在啟動的時候會自動設置Heap size的值,其初始空間(即-Xms)是物理內(nèi)存的1/64,最大空間(-Xmx)是物理內(nèi)存的1/4。可以利用JVM提供的-Xmn -Xms -Xmx等選項可進行設置。Heap size 的大小是Young Generation 和Tenured Generaion 之和。
提示:在JVM中如果98%的時間是用于GC且可用的Heap size 不足2%的時候?qū)伋龃水惓P畔ⅰ?/p>
提示:Heap Size 最大不要超過可用物理內(nèi)存的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,是指內(nèi)存的永久保存區(qū)域,這塊內(nèi)存主要是被JVM存放Class和Meta信息的,Class在被Loader時就會被放到PermGen space中,它和存放類實例(Instance)的Heap區(qū)域不同,GC(Garbage Collection)不會在主程序運行期對PermGen space進行清理,所以如果你的應用中有很CLASS的話,就很可能出現(xiàn)PermGen space錯誤,這種錯誤常見在web服務器對JSP進行pre compile的時候。如果你的WEB APP下都用了大量的第三方jar, 其大小超過了jvm默認的大小(4M)那么就會產(chǎn)生此錯誤信息了。
解決方法:
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 之和)的設置
初始分配的內(nèi)存由-Xms指定,默認是物理內(nèi)存的1/64但小于1G。
最大分配的內(nèi)存由-Xmx指定,默認是物理內(nèi)存的1/4但小于1G。
默認空余堆內(nèi)存小于40%時,JVM就會增大堆直到-Xmx的最大限制,可以由-XX:MinHeapFreeRatio=指定。
默認空余堆內(nèi)存大于70%時,JVM會減少堆直到-Xms的最小限制,可以由-XX:MaxHeapFreeRatio=指定。
服務器一般設置-Xms、-Xmx相等以避免在每次GC 后調(diào)整堆的大小,所以上面的兩個參數(shù)沒啥用。
-Xmn 設置young generation的heap大小
-XX:MinHeapFreeRatio與-XX:MaxHeapFreeRatio設定空閑內(nèi)存占總內(nèi)存的比例范圍,這兩個參數(shù)會影響GC的頻率和單次GC的耗時。-XX:NewRatio決定young與old generation的比例。Young generation空間越大,minor collection頻率越低,但是old generation空間小了,又可能導致major collection頻率增加。-XX:NewSize和-XX:MaxNewSize直接指定了young generation的缺省大小和最大大小。
非堆內(nèi)存 的設置
默認分配為64M
-XX:PermSize設置最小分配空間,-XX:MaxPermSize設置最大分配空間。一般把這兩個數(shù)值設為相同,以減少申請內(nèi)存空間的時間。
再講解和筆記下,JDK下的一些相關看內(nèi)存管理工具的使用:
查看jvm內(nèi)存狀態(tài):
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
這個數(shù)據(jù)顯示Full GC頻繁發(fā)生。
正常情況的例子
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
參數(shù)含義:
S0:Heap上的 Survivor space 0 段已使用空間的百分比
S1:Heap上的 Survivor space 1 段已使用空間的百分比
E: Heap上的 Eden space 段已使用空間的百分比
O: Heap上的 Old space 段已使用空間的百分比
P: Perm space 已使用空間的百分比
YGC:從程序啟動到采樣時發(fā)生Young GC的次數(shù)
YGCT:Young GC所用的時間(單位秒)
FGC:從程序啟動到采樣時發(fā)生Full GC的次數(shù)
FGCT:Full GC所用的時間(單位秒)
GCT:用于垃圾回收的總時間(單位秒)
2 Dump出內(nèi)存
2.1 找出要dump的線程pid
在Linux下,使用ps –aux
2.2 Dump出內(nèi)存使用詳情
可以通過命令:
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的狀態(tài)監(jiān)控工具,監(jiān)控的內(nèi)容有類加載、運行時編譯及GC。
使用時,需加上查看進程的進程id,和所選參數(shù)。以下詳細介紹各個參數(shù)的意義。
jstat -class pid:顯示加載class的數(shù)量,及所占空間等信息。
jstat -compiler pid:顯示VM實時編譯的數(shù)量等信息。
jstat -gc pid:可以顯示gc的信息,查看gc的次數(shù),及時間。其中最后五項,分別是young gc的次數(shù),young gc的時間,full gc的次數(shù),full gc的時間,gc的總時間。
jstat -gccapacity:可以顯示,VM內(nèi)存中三代(young,old,perm)對象的使用和占用大小,如:PGCMN顯示的是最小perm的內(nèi)存使用量,PGCMX顯示的是perm的內(nèi)存最大使用量,PGC是當前新生成的perm內(nèi)存占用量,PC是但前perm內(nèi)存占用量。其他的可以根據(jù)這個類推, OC是old內(nèi)純的占用量。
jstat -gcnew pid:new對象的信息。
jstat -gcnewcapacity pid:new對象的信息及其占用量。
jstat -gcold pid:old對象的信息。
jstat -gcoldcapacity pid:old對象的信息及其占用量。
jstat -gcpermcapacity pid: perm對象的信息及其占用量。
jstat -util pid:統(tǒng)計gc信息統(tǒng)計。
jstat -printcompilation pid:當前VM執(zhí)行的信息。
除了以上一個參數(shù)外,還可以同時加上 兩個數(shù)字,如: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 這里發(fā)生了一次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秒