說說在Java啟動參數上易犯的錯
作者:林昊
前幾年在將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
上一篇:
java.util.concurrent包(4)——讀寫鎖ReentrantReadWriteLock
下一篇:
java.util.concurrent包(6)——CyclicBarrier使用
4月13日雲棲精選夜讀:MongoDB使用常見問題及應用場景全集
《構建實時機器學習係統》一1.5 實時機器學習的分類
ios入門之界麵基礎
Redis開發運維實踐常見運維操作(三)
RDC如何構建一個基於NodeJS的前後端項目
《Vim實用技巧(第2版)》——2.5 能夠重複,就別用次數
spring使用中報Cannot proxy target class because CGLIB2 is not available錯
使用request.getHeader("Referer");得到請求的來源
《Vim實用技巧(第2版)》——1.5 查找並手動替換
Java麵向對象基礎--構造方法私有化