1、瞭解虛擬機內存結構
在進行內存調優之前,我們需要先了java的內存結構,見下圖或查看該遍文章:Java內存導圖
下面我們對關鍵幾個部分進行說明:
- 虛擬機棧:是線程私有的。存儲方法執行時相關信息,每個方法在調用時都會在虛擬機棧中創建一個方法幀,幀中包含了局部變量、參數、運行中間結果等信息。幀數超過限制(-Xss)就會出現StackOverFlow錯誤。在運行過程中如果超過線程分配的內存大小,也會報OOM錯誤。
- 本地方法棧:線程私有的。存放的是native方法幀(基本上同虛擬機棧)。
- 堆:所有線程共享。存放new出來的數組和對象數據,以及類的靜態變量和常量池。
- 元數據區(MateSpaces),它不存在jvm虛擬機中,而是本地內存,默認情況下大小隻與系統內存有關。
2、瞭解GC的過程
在jvm虛擬機中,GC主要還是在Heap上進行。下面我們換結合上圖說明一下GC的過程。
1、新new的對象都將放在Eden區;
2、第一次:Eden區滿或者將滿時,會進行一次Minor GC,不被引用的對象將會被清除,被引用但年齡較大的對象將移動到S0區;
3、第二次:Eden區快滿時,會再次進行Minor GC,Eden和S0區中不被引用的對象將會被清除,同時這兩個區的年齡較大的對象將被移動到S1區(注意:S0和S1在原理上都是保持有一個區是空的)
4、第三次:Eden區快滿時,會進行Minor GC,注意這裏是將對象移動到S0( S1是空的)
5、直到Eden快滿,S0或S1都快滿時,這時把兩個區中年紀較大的對象移動到Old區;
6、按上持續循環,直到Old區也快滿時,Eden已快滿時,S0或S1也快滿時,就會對整個區內區進行Full GC。
注意:元空間也會觸發Full GC執行的
元空間的初始大小是21M,當空間使用超過這個值時,就會進行回收操作。
3、內存參數指南
我們大概瞭解了內存的結構,也清楚GC的主要觸發點,那我們怎麼對這些內存結構進行調整?
-server #啓用JVM的server模式,理好的發揮服務器性能
-Xmx2g #最大Heap可用內存
-Xms2g #初始Heap內存大小(建議生產環境同Xmx一致)
-Xss128k #每個線程Stack內存大小,理論上來說,相同物理內存,值小則能生產更多的線程(當然也要受系統限制的)
-xx:NewRatio=4 #設置新生代與老年代的比值,4表示新生代:老年代=1:4,默認=2(新生代與老年代佔比值爲1:2,新生代佔堆內存1/3)
-xx:SurvivorRatio=4 #設置2個Survivor區和Eden的比值,4表示兩個Survivor:eden=2:4.默認值=8
-Xmn1024m #設置Young Generation佔用的Head的大小爲1g,官方推薦配置爲整個堆的3/8
-XX:NewSize=1g #設置新生代大小
-XX:MaxNewSize=1g #設置新生代最大大小
對新代年設置我們注意到有幾個參數可以配置,那他們的優先級是怎麼樣的呢?
1、最優先:-xx:NewSize -xx:MaxNewSize
2、次之: -Xmn 推薦使用該參數
3、最低: -XX:NewRatio
-XX:OldSize=2g #設置老年代的大小
-XX:MetaspaceSize=128M #初始元空間的大小,默認值爲21MB
-XX:MaxMatespacesSize=256M #最大元空間大小
4、調優原則
1、目標:避免頻繁的Full GC執行
2、Java應用一般都留有JAVA_OPTS或JAVA_OPT_EXT變量,如RocketMQ指定堆大小
rmqserver -e "JAVA_OPT_EXT=-server -Xms128m -Xmx128m -Xmn128m"
3、明確指定Xmx的大小,因爲不指定的情況下,系統會根據程序運行需要動態調整,當需要調整時,會進行一次Full GC。
4、設置Xms=Xmx,避免GC時的動態調整
5、元空間調整,設置-xx:MetaspaceSize,因爲該值默認爲21M,超過這個值,會導致較頻繁的Full GC
6、記錄GC日誌,提供數據分析:-XX:+printGCTimeStamps -XX:+PrintGCDetails -Xloggc:/opt/gc/aa.log
7、官方建議堆空間分配規則
空間 | 命令參數 | 建議 |
Heap | -Xms -Xmx | 3-4倍FullGC後老年代空間佔比 |
新生代 | -Xmn | 1-1.5倍FullGC之後的老年代空間佔比 |
老年代 | 2-3倍FullGC後的老年代空間佔比 |