這幾天,網店系統基礎架構進行了一次大的升級,升級之后例行的進行了壓力測試,以前幾次大的項目發布壓力測試都沒有任何問題,沒想到這次出事故啦,而且是內存泄露?
系統運行環境:
硬件:Intel(R) Xeon(R) CPU 2.0G、4G RAM、Linux 2.6.9-42.ELsmp #1 SMP
軟件:jboss-4.0.5.GA [Java HotSpot(TM) Server VM (build 1.5.0_10-b03, mixed mode)]
JAVA運行參數-server -Xms2048m -Xmx2048m -XX:NewSize=768m -XX:PermSize=128m -XX:MaxPermSize=128m
現象是這樣的:
對系統壓力測試大約4個小時左右,系統突然down掉,拋錯為java.lang.OutOfMemoryError: requested 12 bytes for intptr_t in /BUILD_AREA/jdk1.5.0_10/hotspot/src/share/vm/runtime/deoptimization.cpp. Out of swap space?
由于是晚上進行,所以沒有觀察到任何比較奇怪的現象出現,再次壓力測試,仍然拋錯,但稍微有些不同java.lang.OutOfMemoryError: requested 32756 bytes for ChunkPool::allocate. Out of swap space?
經多次壓力,現象100%可以重現;
解決過程:
1、use jvmstat first
這是SUN自己的性能跟蹤工具,他占用server資源很少;監控的結果令我們失望,因為JVM表現完全正常,我們分配的2G內存還有很多剩余,并沒有耗盡;GC也很正常,沒有出現明顯的Full GC或者是每次GC時間太長的情況;
用top命令查看占用內存不到3G,也還算富裕;而且系統并沒有JNI的使用;為什么會報OutOfMemoryError呢?未果!
2、search Out of swap space
更多都是遭遇了相同的問題,但是好像都沒有一個很可行的解決辦法,甚至有人懷疑是JDK的bug,也有人說可以用-Xss參數設置stack size大小,ss默認大小為512k,但是從監控上看我們的進程也沒有那么多,但還是嘗試了一把,再次壓力問題仍然未解決!
3、try to see heap dump
添加參數-XX:+HeapDumpOnOutOfMemoryError,讓系統出現OutOfMemoryError時將當時JVM內所有heap dump出來,使用jHAT分析;
很可惜,1.5中對該參數的支持超級有限,記錄下來的信息很少,并沒有我們想象的那么多,那么有用,基本上這些信息是無用的;再次失敗!
4、back to OutOfMemoryError
由于jvmstat 能看見的JVM內部信息有限,所以我們打算用專業工具JProfiler來進行詳查;環境搞定之后,再次壓力,不到2小時情況就重現了,但是從JProfiler中觀察到的信息顯示JVM內部的確沒有任何異常,結論和Jconsole觀察后完全一樣,JVM內部沒有任何問題!但為什么會有此錯誤?想不通……
5、focus on java heap with linux
再次search了linux環境下面java heap的相關工作原理及組成信息,有發現了!
其實java heap由2部分組成:其一為我們熟悉的JVM heap,其二為和OS相關的Native heap;
JVM heap完全由GC掌控,我們可以通過參數-Xms、-Xmx指定其大小,并且可以用工具對其進行監控;他管理的東西就是我們所有的Java Object;
而Native heap是平臺相關的,我們既不能設置其使用大小也不能干預他的使用狀態;他管理的東西一般都是很底層的,比如JIT使用的buffer、GC的底層data structures、JNI調用的所有相關對象、SWING/AWT調用需要的buffer和data structures……
由此想到,是否我們分配的JVM heap太大了,于是設置參數變為-Xms1536m -Xmx1536m再次壓力,問題解決了!!!
雖然問題解決了,但是我們還是沒能從根本上解釋此次故障,因為系統可用的內存還有很多,并沒有耗盡?
難道32位的JAVA所能操作的內存只有2G?JVM heap全部占完了會導致Native heap無法allocate memory?后續還需要進一步研究此事;
PS:
1、上面我們提到專業級的性能監控工具JProfiler,他的介紹及其安裝使用可以參考http://blog.chinaunix.net/u/11765/showart_239554.html;
有兩點可以注意下;其一:sh安裝方式下linux默認安裝目錄一般為/usr/local/jprofilerX;其二為${JPROFILER_HOME}/bin/linux-x86目錄不一定要放在單獨項LD_LIBRARY_PATH中,只要classpath中能找到即可;
2、JProfiler、JProbe Profiler、Java Profiler三者是同一個產品么?
答案當然為否;三者都是業內非常優秀的Java性能監控工具,他們分別屬于不同的公司;
JProfiler:http://www.ej-technologies.com
JProbe Profiler:http://www.quest.com;TOAD就是該公司的產品之一;
Java Profiler:http://www.yourkit.com
這也提醒之后遇到問題時第一時間找的不一定是search,可能官方文檔更有效;