JVM GC調優一則–增大Eden Space提高效能
緣起
線上有Tomcat升級到7.0.52版,然後有應用的JVM FullGC變頻繁,在高峰期socket連線數,Cpu使用率都暴增。
思路
思路是Tomcat本身的程式碼應該是沒有問題的,有問題的可能是應用程式碼升級,或者環境改變了,總之Tomcat的優先順序排在最後。
先把應用的heap dump下來分析下:
jmap -dump:format=b,file=path pid
用IBM的Heap Analyser分析,發現dubbo rpc呼叫的RpcInvocation物件和taglibs的SimpleForEachIterator物件佔用了很大部分記憶體。
正常來說,這兩種型別的物件都應該可以很快被回收掉,怎麼會佔用了那麼大的記憶體空間?是不是有別的物件引用了它們,導致不能釋放?
再仔細分析,發現RpcInvocation物件都是root refer的,也就是根物件,正常來說根物件應該可以很快就被回收掉的,為什麼在記憶體中會有那麼多物件?
再檢視應用的JVM引數:
1
|
-Xms2g
-Xmx2g -Xmn256m -XX:SurvivorRatio= 8
-XX:ParallelGCThreads= 8
-XX:PermSize=512m -XX:MaxPermSize=512m -Xss256k -XX:-DisableExplicitGC -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled |
首先發現應用的新生代,即-Xmn256m 設定得太小了。對照上面RpcInvocation物件佔用了226M,SimpleForEachIterator佔用了267M記憶體。
顯然在新生代裡,沒辦法放下那麼多的物件,這些物件必然是被放到老生代(old space)裡去了。
既然RpcInvocation物件和SimpleForEachIterator物件應該都是可以很快被回收了,那麼思路變成,觸發一下線上的FullGC,看下物件有沒有被回收。
在觸發之前,先用jmap -histo pid統計下物件的數量:
34: 136762 4376384 com.alibaba.dubbo.rpc.RpcInvocation
129: 16345 392280 org.apache.taglibs.standard.tag.common.core.ForEachSupport$SimpleForEachIterator
用 jmap -histo:live <pid> 觸發Full GC之後:
294: 625 20000 com.alibaba.dubbo.rpc.RpcInvocation
495: 292 7008 org.apache.taglibs.standard.tag.common.core.ForEachSupport$SimpleForEachIterator
果然數量大大的減少了。
所以結論比較明顯了,新生代(Young generation)的空間太小,導致有一些本應該可以很快就被回收的物件被放到了老生代(Old generation)裡,導致老生代上漲很快,頻繁Full GC。
於是想辦法增加新生代的大小,把JVM引數改為:
1
|
-Xms2g
-Xmx2g -XX:ParallelGCThreads= 8
-XX:PermSize=256m -XX:MaxPermSize=512m -Xss256k -XX:-DisableExplicitGC -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled |
因為觀察到PermSize實際上只用了不到200M,沒有必要設定為512M,浪費記憶體,所以改為 -XX:PermSize=256m -XX:MaxPermSize=512m 。
另外,把新生代最大限制-Xmn256m 去掉。因為預設的NewRatio = 2,即除了PermSize,新生代大約佔記憶體的1/3,即約(2048 – 256) /3 = 597M。和原來相比增大了一倍不止。
修改上線之後,觀察發現Old Space增長緩慢,FullGC次數大大減少,時間在50ms下,Yong GC都在10ms下,達到了想要的效果。
簡單的GC過程分析
首先來看一張GC的模型圖,很形象:
簡單來說,對於GC,我們瞭解到這些資訊就足夠了。
大部分新物件在Eden Space上分配,當Eden Space滿了,則要用到Survivor Space來回收。YGC的演算法是很快的。
多次YGC之後,還存活的物件就會被移到Old Generation(old space)上,當Old Generation滿了的時候,就會FGC,FGC有通常比較慢。
Permanent Space只要你在開始時分配了足夠大的空間,那它可以不用管。
我們可以得出一些結論:
- 合理減少物件進入老生代;
- Old Space可能會一直增長,有時沒有辦法避免不讓物件進入Old Space,當然也有一些程式是從來都不執行FGC的;
- 是不是盡全力防止物件進入老生代?顯然不是,有些物件如果長久存在在新生代裡,顯然加重了YGC的負擔,多次YGC之後仍然存活的物件顯然應該放到Old Space裡。
理想的GC/記憶體使用情況
總結下來,可以發現,理想的GC情況應該是這樣的:
Old Space增長緩慢,FullGC次數少,FullGC的時間短(大部情況應該要在1秒內)。
總結:
儘量少加上一些預設引數。這點我很贊同RednaxelaFX的看法,配置了預設引數除了讓後面調優的人蛋疼之外,沒有太多的幫助。
GC調優就是一個取捨權衡的過程,有得必有失,最好可以在多個不同的例項裡,配置不同的引數,然後進行比較。
有很多命令列工具或者圖形工具可以使用,好的工具事半功倍。
參考:
http://www.alphaworks.ibm.com/tech/heapanalyzer IBM Heap Analyser
http://hllvm.group.iteye.com/group/topic/27945 JVM調優的”標準引數”的各種陷阱,RednaxelaFX 出品,強列推薦
http://www.taobaotesting.com/blogs/2392 Java效能剖析1——JVM記憶體管理與垃圾回收
http://www.oschina.net/translate/using-headless-mode-in-java-se 在 Java SE 平臺上使用 Headless 模式
轉載:http://www.importnew.com/19308.html?utm_source=tuicool&utm_medium=referral
相關文章
- 探探Java之 JVM GC與調優JavaJVMGC
- JVM的Eden由來JVM
- GC調優記錄(一)GC
- 深入理解JVM效能調優JVM
- Java效能調優準則Java
- Oracle效能調優原則Oracle
- JVM效能調優與實戰篇JVM
- 《java學習三》jvm效能優化-------調優JavaJVM優化
- JVM面試問題系列:JVM 配置常用引數和常用 GC 調優策略JVM面試GC
- JVM效能優化,提高Java的伸縮性JVM優化Java
- 【深入理解JVM】8、JVM實戰調優+GC演算法+JVM調優如何定位問題+常見的定位JVM優化命令【面試必備】JVMGC演算法優化面試
- JVM效能優化 (一) 初識JVMJVM優化
- JVM效能調優,記憶體分析工具JVM記憶體
- JVM調優JVM
- JVM調優——JVM監控工具jvisualvm的使用及GC外掛安裝JVMLVMGC
- 效能調優(cpu/IO/JVM記憶體分析)JVM記憶體
- JVM調優筆記(一)--Nacos GC引發的服務批次下線問題JVM筆記GC
- Spark效能調優——9項基本原則Spark
- JVM效能優化(一)JVM技術入門JVM優化
- JVM調優總結-調優方法JVM
- Java 效能調優:最佳化 GC 執行緒設定JavaGC執行緒
- JVM調優策略JVM
- JVM效能調優與實戰進階篇-上JVM
- JVM效能優化JVM優化
- JVM調優總結(十)-調優方法JVM
- Mysql 效能調優 一 1MySql
- Mysql 效能調優 一 2MySql
- Mysql 效能調優 一 3MySql
- 聊一聊 JVM 的 GCJVMGC
- 關於GC原理和效能調優實踐,看這一篇就夠了!GC
- JVM調優總結(一)-- 一些概念JVM
- 阿里面試100%問到,JVM效能調優篇阿里面試JVM
- Java GC 專家系列3:GC調優實踐JavaGC
- jvm系列(五):tomcat效能調優和效能監控(visualvm)JVMTomcatLVM
- jvm系列(十):如何優化Java GC「譯」JVM優化JavaGC
- Java GC 專家系列5:Java應用效能優化的原則JavaGC優化
- JVM調優推薦JVM
- JVM調優淺談JVM