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