背景,服務器上的一個JAVA服務進程忽然掛掉,查看產生了崩潰日誌,以下:java
# Set larger code cache with -XX:ReservedCodeCacheSize= # This output file may be truncated or incomplete. # # Out of Memory Error (os_linux.cpp:2673), pid=28610, tid=139813184919296
日誌分析緣由很簡單,服務器的內存不夠用,致使進程崩潰linux
JAVA涉及到內存不夠用分兩種狀況:ubuntu
1, 當超出JVM的分配的內存時,JAVA進程並不會退出只是結束當前的線程服務器
2, 當服務器內存不夠時,linux殺死使用內存的一個進程測試
很簡單,但很容易忽略,由於在啓動JAVA進程時,服務器檢查的是當前內存,並非可用額度。如,服務器有1G內存,而啓動了兩個1G內存的JAVA服務是不會報錯的,但當內存緊張時,linux會kill任意JAVA服務,形成影響線程
我用代碼重現模擬下這種狀況日誌
準備以下code
服務器1臺,1G內存對象
模擬JAVA進程,以每秒遞增10m內存分配的去榨乾服務器進程
服務器內存總量
發現整個可用內存大小在 3G附近(小於)
加入一個JAVA程序,開啓一個線程以每秒10m的內存去申請內存
代碼以下:
模擬第一種狀況:
運行 java -Xmx1024m -Xms1024m org.hejinbin.memory.test.TestStepByStep
觀察JVM垃圾回收狀況,發如今程序把JVM的內存的heap慢慢耗光
直到再也沒法分配10m大小的對象,而後程序輸出
觀察後發現, 知道JVM內存被榨乾,這條JAVA線程已經被中止,但JAVA進程
也是存活的,並且等待下一次垃圾回收,死去線程的內存將被回收,系統恢復。 形成的影響僅僅是影響本次請求。
模擬第二種狀況:
我模擬3個JAVA服務進程,每一個進程分配1G, 而後同時遞增分配內存
開啓多個linux窗口同時運行:
java -Xmx1024m -Xms1024m org.hejinbin.memory.test.TestStepByStep
一段時間後:
the total use : 830m
the total use : 840m
the total use : 850m
the total use : 860m
Killed
其中一條線程被kill了
另外兩個進程繼續跑,知道線程異常,固然最後兩個進程也不會掛掉
另:觀察到kswapd0佔用CPU 高,交互虛擬內存
結論:
1, 當超出JVM的分配的內存時,JAVA進程並不會退出只是結束當前的線程
2, 當服務器內存不夠時,linux殺死使用內存的一個進程
3, 把系統拆分紅多個服務部署在同一臺機時須要特別注意,JVM啓動時分配的內存只是申請(其實體如今VIRT),當一臺服務器運行多個JAVA進程時請保留足夠的可用內存 (大於分配給各個JVM的進程之和)
接下來的問題:
1, 如何確認是由於內存過大被linux系統kill掉
答: 能夠去linux的日誌在/var/log/syslog 裏能找到日誌,如: grep "Out of memory" syslog , 能找到剛剛被殺線程的緣由
相似以下
Dec 29 21:43:42 ubuntu kernel: [81664.250008] Out of memory: Kill process 4053 (java) score 919 or sacrifice child
2,除了內存佔用過大被kill,還有哪一種緣由會被kill
答: CPU時間佔用太久也是會被殺掉,詳情下一篇模擬
3,爲何我模擬的沒有產生hs_err_pid*.log日誌?
答: 由於日誌是再進程嘗試重啓後產生, 當系統內存不足,系統kill掉進程,此時守護進程又嘗試拉起進程,故產生了err日誌。能夠在進程A佔用了大部份內存時手工重啓線程B模擬,能在JAVA程序的運行生成該日誌
4, 一個JAVA進程到底佔用多少內存?
答:JAVA進程≈ JVM(JVM的代碼區)+JVM(JVM的數據區)棧+JAVA棧(-Xss*線程數)+JAVA的NIO(對外內存)+JAVA堆(-Xmx)
另外一種改進, 把SWAP關了再測試,更精確
歡迎關注個人公衆號,專一重現各類線上的BUG
或搜 「包子的實驗室」