系統在灰度環境上變更時發現JVM啟動報錯,詳細檢查JVM配置參數,發現新境了如下配置:
-XX:+UseAdaptiveSizePolicy和-XX:+UseConcMarkSweepGC
初步猜想是JVM參數配置的問題,於是通過jmap -heap查看系統堆棧使用情況,如下:
Heap Configuration: MinHeapFreeRatio = 40 MaxHeapFreeRatio = 70 MaxHeapSize = 10737418240 (10240.0MB) NewSize = 2147483648 (2048.0MB) MaxNewSize = 2147483648 (2048.0MB) OldSize = 5439488 (5.1875MB) NewRatio = 2 SurvivorRatio = 4 PermSize = 21757952 (20.75MB) MaxPermSize = 134217728 (128.0MB) Heap Usage: unknown generation type: capacity = 0 (0.0MB) used = 0 (0.0MB) free = 0 (0.0MB) NaN% used unknown generation type: capacity = 0 (0.0MB) used = 0 (0.0MB) free = 0 (0.0MB) NaN% used Perm Generation: capacity = 60878848 (58.05859375MB) used = 37927152 (36.17015075683594MB) free = 22951696 (21.888442993164062MB) 62.29939173619054% used
從打印的堆棧信息上看已發現異常
一、JVM分析
1、源碼查看
分析jdk的management.cpp代碼,發現在計算堆內存區大小的時候,對commit都進行了累加,但是在max_size沒有定義(無效,從MemoryPool獲取)的情況下,total_max沒有累加,導致commited比max大。
修復后的代碼見: http://hg.openjdk.java.net/hsx/hsx25/hotspot/file/a70566600baf/src/share/vm/services/management.cpp的方法JVM_ENTRY中。
對比:
原來只處理:
if (!has_undefined_max_size) { total_max += u.max_size(); }
修復該問題的方式:增加代碼處理沒有定義init和max的情況
if (has_undefined_init_size) { total_init = (size_t)-1; } if (has_undefined_max_size) { total_max = (size_t)-1; }
2、jmap不能獲取數據原因
jmap出現不能獲取的原因:(UseAdaptiveSizePolicy + CMS同時使用會出現) (該狀況源碼: sun/jvm/hotspot/memory/GenerationFactory.java ):
try { return (Generation) ctor.instantiateWrapperFor(addr); } catch (WrongTypeException e) { return new Generation(addr) { public String name() { return "unknown generation type"; ……
二、問題產生原因
目前確認是jvm的bug,初步確認版本為1.6_u30以上,包括1.7都存在該問題.jdk6.30以下版本還未確認(使用1.6_u25版本后,目前還沒有復現問題)---1.6.30以上到1.7的全部版本已經確認有該問題,jdk8修復,其他版本待驗證
簡要原因分析請參見: http://blog.csdn.net/axman/article/details/8667351
在使用cms算法下,如果開啟參數UseAdaptiveSizePolicy,則每次minor gc后會重新計算eden,from和to的大小,計算過程依據的是gc過程統計的一些數據,計算后的eden+from+to不會超過Xmx,同時from和to一般是不相等(初始化的時候from和to是相等的)。主要問題在於計算完后,如果eden變大,ContiguousSpacePool里面的max_eden_size並沒有被更新,還是最開始時候的值,這樣導致jvm在通過call_special調用java.lang.management. MemoryUsage的構造函數的時候會產生exception,產生exception的原因是eden的committed 大於 eden的max_size,導致返回java.lang.management. MemoryUsage對象失敗,最終導致產生顯示異常。
三、解決辦法
可以先設置 –XX:-UseAdaptiveSizePolicy來workaround,JDK的版本:sun jdk出問題后的版本目前看是只有jdk8修復;openjdk是hs25修復。