您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關如何理解Java進程的OOMKiller,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
登上機器后,查看應用和中間件日志,確實沒有看到問題。我懷疑是JVM OOM了但沒有配置輸出,正想加上OOM時的堆棧輸出參數,但發現應用啟動命令已經包含類似的參數:-XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=./hs_err_pid<pid>.log -XX:HeapDumpPath=./java_pid<pid>.hprof
。 看來從應用層面查不到什么了,那就再看看系統日志吧。 使用dmesg -T | grep java
查看系統日志 果然,從dmesg
倒數第二行返回的信息來看,確實是由于oom導致的,但是這里的oom并非JVM的oom,而是Linux系統的oom。 >Killed process xxx(java), total-vm:7539096kB, anon-rss:3919048kB file-rss:17312kB, shmem-rss:0kB。
在這臺物理內存為4GB的機器上, `total-vm`是已經分配給java進程的虛擬內存數量(7539096kb=7.02GB) `anon-rss`是java進程物理內存使用量(3919048kB=3.65GB) `file-rss`是java進程映射到設備或文件系統的使用量(17312kB=16.51MB)
網上已經有很多介紹Linux oom killer的文章了,這里只簡單概括:Linux系統在分配物理內存時,如果內存不足(什么時候不足?)oom killer會選擇oom score得分最高的進程殺掉以釋放內存。
看起來問題很明確了,是Linux的oom killer殺掉了Java進程。但是我還是有個疑問,同樣是OOM,為什么沒有觸發JVM的OOM呢?帶著這個疑問,繼續了解了Linux的內存分配機制。
以下簡單說一下我的理解,真實分配機制要復雜的多。有不對的地方請大家指正。 Linux有一套虛擬內存機制,當進程向系統申請內存時,總體上系統可以有兩種方式答復進程: a.檢查是否真的還有足夠的內存(實際值計算一般等于Swap+RAM*overcmmit_ratio)滿足需求,如果有就滿足分配,如果沒有就以分配失敗答復集成; b.不檢查,直接分配。
這里的分配
指的都是進程的虛擬內存地址的增長。這里的b叫做"Overcommit",也就是發生了超過系統可接受內存范圍的分配。這里的b其實是基于一種樂觀的估計,因為申請的內存不一定用到。但是一旦進程需要使用所能提供的實際內存時,就會導致OOM,此時oom killer就會通過排序oom score犧牲掉一個或多個得分最高的進程,以此來釋放內存。
再強調一次理解Overcommit的關鍵點,內存申請不等于內存分配,內存使用時才真正分配。
那么JVM的OOM呢?
>One common indication of a memory leak is the java.lang.OutOfMemoryError
exception. Usually, this error is thrown when there is insufficient space to allocate an object in the Java heap. In this case, The garbage collector cannot make space available to accommodate a new object, and the heap cannot be expanded further. Also, this error may be thrown when there is insufficient native memory to support the loading of a Java class. In a rare instance, a java.lang.OutOfMemoryError
may be thrown when an excessive amount of time is being spent doing garbage collection and little memory is being freed. 摘自Oracle內存泄露說明相關文章
簡單來說,發生JVM OOM的原因有: Java堆內存不足,垃圾回收器不能給新對象騰地兒,堆也無法擴展(Java heap space); 本地內存不足以支持加載Java類(Metaspace or Perm); 不常見地,過長時間的垃圾回收沒有釋放多少內存也會導致OOM(GC Overhead limit exceeded)。 回頭看看,其實是Linux系統把Java進程給“騙”了。當開啟了允許overcommit的策略時,如果Java進程或其他任何進程申請了可能過多(超過系統能提供的)虛擬內存時,只要系統內存還足夠使用,Java進程并不會發生OOM。而當Linux系統發現分配內存真的不夠時,就會把oom score最靠前的Java進程悄無聲息地干掉(kill -9
)。
>Java老農:“爺,給俺批點地,俺要種瓜。” Linux老爺:“爺我高興,給你4096畝地,隨便耍!” Java老農種地中...1...2...4....1024... Java老農:“沒有蟲子騷擾太開心啦,我要這樣到永...” Java卒 Linux老爺:“有沒有人要地呀,老爺我有的是地啊!”
分析完原因,其實有很多解決方案。
如果有資源,就先嘗試最簡單的辦法吧。
現在的機器一般都是容器或虛擬機上只要只跑一個應用,如果發生OOM,最根本的解決之道還是要回歸到代碼上。
讓系統在申請時就報錯,保守也保險。及早暴露問題,及早進行修復。同時可以修改overcommit_ratio的值改變CommitLimit。 配置參見附錄。
對于一些多應用進程混用的機器,可以保護關鍵進程不被kill掉。 sudo echo -1000 > /proc/$pid/oom_score_adj
修改panic_on_oom
參數可以關閉OOM Killer。玩玩還可以,線上系統不建議用,否則系統死給你看。
可在/proc/sys/vm/overcommit_memory
配置或查看。
>0:默認值。啟發式策略,比較嚴重的Overcommit將不能滿足,而輕微的Overcommit將被允許。另外,root能Overcommit的值比普通用戶要稍微多些,一般為3%。 1:允許Overcommit,這種策略適合那些不能承受內存分配失敗的應用,比如某些科學計算應用。(本文中涉及的系統就是開啟的這個策略) 2:禁止Overcommit,在這個情況下,系統所能分配的內存不會超過下面的CommitLimit
,計算方法為Swap + RAM * /proc/sys/vm/overcmmit_ratio
,默認50%,可調整),如果這么多資源已經用光,那么后面任何嘗試申請內存的行為都會返回錯誤,這通常意味著此時沒法運行任何新程序。
grep -i commit /proc/meminfo CommitLimit: 6201492 kB # 虛擬內存限定值 Committed_AS: 5770836 kB # 已分配內存,如果大于CommitLimit說明開啟了允許Overcommit的策略
# 進程oom得分,0不kill cat /proc/{pid}/oom_score # 進程oom調整兼容,計算時一般會以oom_score_adj替換 cat /proc/{pid}/oom_adj # 用戶打分調整。最小值-1000,將會禁止oom killer殺此進程。 #取值從-1000到1000,表示對最終得分的折扣到懲罰。 cat /proc/{pid}/oom_score_adj
看完上述內容,你們對如何理解Java進程的OOMKiller有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。