一、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後的佔用內存大小