閱讀632 返回首頁    go 阿裏雲 go 技術社區[雲棲]


說說在Java啟動參數上易犯的錯

作者:林昊

mistake

前幾年在將OS從32 bit升級到64 bit,以及虛擬機的內存調整到8G後,我把應用的Java啟動參數重新寫了一版,作為目前大部分Java應用的默認啟動參數模版,這幾年下來,發現在這個標準版的啟動參數上還是犯了一些錯誤的。

 

1. -XX:+DisableExplicitGC
Java在實現RMI Server的時候會通過定時的調System.gc來強製做GC(即使程序裏沒用到RMI也會被啟動),這個動作非常煩人,另外也是為了避免應用代碼上顯式去調用System.gc導致一些沒必要的GC動作產生,所以當時就直接加上了這個參數。

現在來看,這個參數有個挺大的問題是,Direct ByteBuffer所占用的內存以及FileChannel.map所占用的內存當達到了他們的最大閾值時,需要依賴調用System.gc來強製釋放下,如果加上了這個啟動參數,就意味著這個強製的釋放就無效了,這會導致的一個問題是,當old gen還沒到達觸發full gc/cms gc的條件,而堆外的Direct ByteBuffer/FileChannel.map占用的空間又超過了它們的最大閾值時,就會直接導致OOM,而這種情況下很有可能其實是可以借助顯式調用System.gc來釋放出足夠的空間,不過話說我仍然覺得這是JDK設計上應該改進的一點,不應該在這個時候需要依賴System.gc來管理堆外的空間,大家可以翻下FileChannel.map的代碼就會發現那裏在等待System.gc執行的結果是寫S的等待100ms,事實上很少有full gc/cms gc可以在100ms完成。

 

不過鑒於上麵的狀況,如果應用裏有使用到不少Direct ByteBuffer或FileChannel.map的話,建議還是不要開啟-XX:+DisableExplicitGC,如果是cms gc的,還是改為加上這個參數-XX:+ExplicitGCInvokesConcurrent,另外如果有RMI Server這種定時GC影響的,再調整下-Dsun.rmi.dgc.client.gcInterval和-Dsun.rmi.dgc.server.gcInterval這兩個時間吧,時間單位是ms,也可以設置為Long.MAX_VALUE。

 

2. 缺少-XX:+UseCMSInitiatingOccupancyOnly
由於我們的Java應用的heap基本都是大於4G的,所以都是用的CMS,當時我在寫啟動參數的時候一直猶豫要不要加上-XX:+UseCMSInitiatingOccupancyOnly這個參數,一猶豫就沒加,但事實上後來碰到了不少應用由於JVM自行觸發CMS GC的機製導致CMS GC頻繁,所以建議用CMS GC的場景下還是加上這個參數更穩妥。

 

3. -XX:MaxDirectMemorySize
話說在寫啟動參數的時候我都壓根不知道這參數(要知道Java到底有哪些啟動參數可用,以及默認值是多少,最靠譜的方法是在啟動參數上加-XX:+PrintFlagsFinal或用jinfo -flags [pid]來查看),後來是由於有一次出現了有應用物理內存被耗光,排查的時候才發現是Direct ByteBuffer這塊默認的大小是heap size,所以在有些情況下可能會出現Direct ByteBuffer這裏占用了大量的空間,但heap這邊又還不到觸發Full gc/CMS gc的條件,就會有可能導致物理內存被耗光。
因此對於遠程交互比較多的應用,建議還是加上這個參數,合理控製大小,不要讓heap size+Direct Memory Size就把物理內存給耗光了。

 

ps: 關於Java常見問題的排查方法,重新專門寫了一個PPT,涵蓋了以下幾種常見的Java問題的排查方法:
1. 類加載問題,例如NoSuchMethodException;
2. 內存問題,例如各種OOM;
3. 應用無響應問題,例如http訪問後返回499;
4. CPU利用率問題,例如us耗盡;
5. Java進程退出問題。

最後更新:2017-04-03 16:49:21

  上一篇:go java.util.concurrent包(4)——讀寫鎖ReentrantReadWriteLock
  下一篇:go java.util.concurrent包(6)——CyclicBarrier使用