初步瞭解jvm的內存分配,gc以及簡單的jvm內存配置

一、jvm常見問題

1、內存不足,這裏主要是指堆內存不足,會引發OOM,out of memory
2、cpu飆升,系統卡頓

二、簡單瞭解jvm內存和gc

在此之前,我們應該對jvm的內存分配,以及基本的gc機制有一定的瞭解。
在這裏插入圖片描述
簡單說,jdk8以後,堆區就分爲新生代老年代,圖中的Permanent永久代被移除了,用元空間代替。

默認的,新生代 ( Young ) 與老年代 ( Old ) 的比例的值爲 1:2 ( 該值可以通過參數 –XX:NewRatio 來指定 ),即:新生代 ( Young ) = 1/3 的堆空間大小。

老年代 ( Old ) = 2/3 的堆空間大小。其中,新生代 ( Young ) 被細分爲 Eden 和 兩個 Survivor 區域,這兩個 Survivor 區域分別被命名爲 from 和 to,以示區分。

默認的,Eden : from : to = 8 : 1 : 1 ( 可以通過參數 –XX:SurvivorRatio 來設定 ),即: Eden = 8/10 的新生代空間大小,from = to = 1/10 的新生代空間大小。

創建的對象首先在新生代,當新生代使用內存到一定值,觸發一次minor gc,回收機會把Eden和其中一個Survivor 中存活的對象,複製到另一個空的Survivor 中,然後清空前面兩個區。

新生代的Minor GC 和老年代的Full GC都會導致stop the world,就是停止所有線程

gc觸發條件

1、Minor GC 觸發的條件

當Eden區滿的時候,會觸發Minor GC

2、Full GC觸發的條件

(1)調用System.gc的時候,系統建議執行Full GC,但是不是必然執行的。

    此方法只是建議使用,很多情況下會觸發Full GC,從而增加GC的頻率,增加了服務間歇性停頓的次數。建議能不使用此方法就別使用,讓虛擬機自己去管理它的內存,通過-XX:DisableExplicitGC來禁止RMI調用System.GC

(2)老年代空間不足的時候會觸發Full GC

    當新生代對象沒有通過GC回收之後轉入到老年代或者創建大對象,大數組的時候老年代纔會出現空間不足的現象,此時會執行Full GC。如果在執行Full GC之後空間還是不足,就會拋出java.lang.OutOfMemoryError: Java heap space錯誤。

    爲了避免上述情況出現而引起的Full GC,虛擬機調優的時候儘量做到讓對象在Minor GC(新生代垃圾回收)階段被回收,讓對象在新生代多存活一段時間,不要創建過大的對象和數組。

(3)方法區空間不足的時候會觸發Full GC

    方法區在JDK1.7也稱爲永久代,主要存放一些class的信息、常量、靜態變量等數據,當系統中要加載的類、反射的類和調用的方法比較多的時候,永久代可能會被佔滿,在沒有配置採用CMS GC的情況下也會去觸發執行Full GC。如果經過Full GC之後對象仍然不能回收,那麼JVM就會拋出java.lang.OutOfMemoryError: PermGen space。

    爲了避免永久代(Perm Gen)被沾滿觸發Full GC的現象,可以採用的方法是增大永久代(Perm Gen)的空間或者轉爲使用CMS GC的方法。

(4)通過Minor GC後進入到老年代的平均大小大於老年代的可用內存的時候會觸發Full GC

    Hotspot爲了避免由於新生代對象晉升到老年代導致老年代空間不足的現象,在進行Minor GC(新生代垃圾回收)的時候,做個一個判斷,如果之前統計所得到的Minor GC晉升到老年代的平均大小大於老年代的剩餘的空間,那麼就會觸發Full GC。

    例如:當程序第一次觸發Minor GC的時候,有6M的對象晉升到了老年代,那麼當下一次Minor GC的時候,首先會去檢查老年代剩餘的空間大小是否小於6M,如果小於6M,則會去執行Full GC。

(5)由Eden區、From Space區向To Space區複製的時候,對象大小大於To Space可用內存,則把該對象轉存到老年代,並且老年代的可用內存小於該對象的大小的時候會觸發Full GC

(6)堆中分配很大的對象導致觸發Full GC

    大對象主要是指大量連續的內存空間的java 對象,例如很長的數組,此種對象會直接進入到老年代。而老年代雖然有很大的剩餘空間,但是無法找到足夠大的連續空間來分配給當前對象,此種情況下就會觸發JVM進行Full GC

三、JVM參數配置

上面說了jvm的內存分配和gc回收機制,那麼內存溢出,OOM的原因,首先考慮是不是配置的內存不夠。jvm默認配置的最大堆內存是64M,這對一個普通的項目是遠遠不夠的。需要我們自己設置參數

3.1 參數含義

-Xmx512m									最大總堆內存
-Xms512m									初始總堆內存,一般將它設置的和最大堆內存一樣大,這樣就不需要根據當前堆使用情況而調整堆的大小了
-Xmn192m									新生代堆內存,sun官方推薦爲整個堆的3/8
-XX:NewRatio 							老年代和新生代和的大小比值,爲2表示新生代佔堆區的1/3
-XX:SurvivorRatio						Eden區和一個survivor區的大小比值,爲8表示一個survivor區佔新生代的1/10
-XX:PermSize=128m					永久代堆的初始大小,jdk8以後永久代移除了
-XX:MaxPermSize=128m			永久代堆的最大大小,jdk8以後永久代移除了
----jdk8以後-----
-XX:MetaspaceSize					初始空間大小,達到該值就會觸發垃圾收集進行類型卸載,同時GC會對該值進行調整:如果釋放了大量的空間,就適當降低該值;如果釋放了很少的空間,那麼在不超過MaxMetaspaceSize時,適當提高該值。 
-XX:MaxMetaspaceSize				最大空間,默認是沒有限制的

3.2 tomcat設置

catalina.sh文件中,找到cygwin=false,在這一行的前面加入參數,具體如下

# vi TOMCAT_HOME/bin/catalina.sh
JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxNewSize=512m"

3.3 jar設置

啓動的時候,在java -jar xxx.jar中加入, 如:

java -jar -Xmx512M -Xms256M -Xmn128M   zgd-web.jar

四、簡單調優

簡單的jvm優化配置原則

通用法則1:

將java堆的初始值-Xms和最大值-Xmx設置爲老年代活躍數據大小的3~4倍

通用法則2:

永久帶的初始值-XX:PermSize及最大值-XX:MaxPermSize應該比永久代活躍數據大1.2~1.5倍

補充法則:

新生代空間應該爲老年代空間活躍數據的1~1.5倍

如果java堆的初始值及最大值爲活躍數據的3~ 4,新生代爲活躍數據的1~ 1.5倍時,老年代應設置爲活躍數據大小的2~3倍

參考:《java性能優化權威指南》

這裏說的活躍數據,是fullgc後的佔用內存大小

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