JAVA系列之JVM內存調優

一、前提JVM性能調優牽扯到各方面的取舍與平衡,往往是牽一發而動全身,需要全盤考慮各方面的影響 。在優化時候,切勿憑感覺或經驗主義進行調整,而是需要通過系統運行的客觀數據指標,不斷找到最優解 。同時,在進行性能調優前 , 您需要理解并掌握以下的相關基礎理論知識:

1、JVM垃圾收集器和垃圾回收算法2、JVM性能監控常用工具和命令3、JVM運行時數據區域4、能夠讀懂gc日志5、內存分配與回收策略
二、JVM內存結構
JAVA系列之JVM內存調優

文章插圖
從上圖可以看出 , 整個JVM內存是由棧內存、堆內存和永久代構成 。
年輕代(New generation) = eden + s0 + s1堆內存 = 年輕代 + 老年代(Old generation)JDK1.8以前: JVM內存 = 棧內存 + 堆內存 + 永久代JDK1.8以后: 由元空間取代了永久代,元空間并不在JVM中,而是使用本地內存 。因此JVM內存 = 棧內存 + 堆內存
1、棧內存棧內存歸屬于單個線程,也就是每創建一個線程都會分配一塊棧內存,而棧中存儲的東西只有本線程可見,屬于線程私有 。棧的生命周期與線程一致,一旦線程結束,棧內存也就被回收 。棧中存放的內容主要包括:8大基本類型 + 對象的引用 + 實例的方法
JAVA系列之JVM內存調優

文章插圖
2、堆內存堆內存是由年輕代和老年代構成,JDK1.8以后,永久代被元空間取代,使用直接內存 , 不占用堆內存 。堆內存是Jvm中空間最大的區域,所有線程共享堆 , 所有的數組以及內存對象的實例都在此區域分配 。我們常說的垃圾回收就是作用于堆內存 。
JAVA系列之JVM內存調優

文章插圖
Eden區占大容量,Survivor兩個區占小容量 , 默認比例是8:1:1
3、永久代(元空間)這個區域是常駐內存的 。用來存放JDK自身攜帶的Class對象 。Interface元數據,存儲的是Java運行時的一些環境 。這個區域不存在垃圾回收!關閉虛擬機就會釋放這個區域的內存 。當發現系統中元空間占用內存比較大時,排查方向是否加載了大量的第三方jar包,Tomcat部署了太多應用,大量動態生成的反射類等 。
三、JVM常用參數首先JVM內存限制于實際的最大物理內存 , 假設物理內存無限大的話,JVM內存的最大值跟操作系統有很大的關系 。簡單的說就32位處理器雖然可控內存空間有4GB,但是具體的操作系統會給一個限制,這個限制一般是2GB-3GB(一般來說Windows系統下為1.5G-2G , Linux系統下為2G-3G),而64bit以上的處理器就不會有限制 。
1、堆大小設置java -server -Xmx4g -Xms4g -Xmn2g –Xss128k
-Xmx4g:設置JVM最大可用內存為4g 。-Xms4g:設置JVM最小可用內存為4g 。一般配置為與-Xmx相同,避免每次垃圾回收完成后JVM重新分配內存 。-Xmn2g:設置年輕代大小為2G 。整個堆大小=年輕代大小 + 年老代大??,所以札e竽昵崠螅?將會減小年老代大小 。-Xss128k:設置每個線程的堆棧大小 。JDK5.0以后每個線程默認大小為1M,以前每個線程大小為256K 。根據應用的線程所需內存大小進行調整 。在相同物理內存下,減小這個值能生成更多的線程 。
java -server -Xmx4g -Xms4g -Xmn2g –Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxMetaspaceSize=16m -XX:MaxTenuringThreshold=0
-XX:NewRatio=4: 設置年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代) 。設置為4,則年輕代與年老代所占比值為1:4,年輕代占整個堆棧的1/5-XX:SurvivorRatio=4: 設置年輕代中Eden區與Survivor區的大小比值 。設置為4 , 則兩個Survivor區與一個Eden區的比值為2:4,一個Survivor區占整個年輕代的1/6-XX:MaxMetaspaceSize=16m: 設置元空間最大可分配大小為16m 。-XX:MaxTenuringThreshold=0: 設置垃圾最大年齡 。如果設置為0的話,則年輕代對象不經過Survivor區,直接進入年老代 。對于年老代比較多的應用,可以提高效率 。如果將此值設置為一個較大值 , 則年輕代對象會在Survivor區進行多次復制,這樣可以增加對象再年輕代的存活時間,增加在年輕代即被回收的概率 。
2、垃圾回收器選擇JVM給了三種選擇:串行收集器、并行收集器、并發收集器,但是串行收集器只適用于小數據量的情況,所以這里的選擇主要針對并行收集器和并發收集器 。默認情況下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在啟動時加入相應參數 。JDK5.0以后 , JVM會根據當前系統配置進行判斷 。

推薦閱讀