Linux 內(nèi)核有個機制叫OOM killer(Out Of Memory killer)峦阁,該機制會監(jiān)控那些占用內(nèi)存過大,尤其是瞬間占用內(nèi)存很快的進程,然后防止內(nèi)存耗盡而自動把該進程殺掉袒哥。內(nèi)核檢測到系統(tǒng)內(nèi)存不足韵丑、挑選并殺掉某個進程的過程可以參考內(nèi)核源代碼linux/mm/oom_kill.c,當系統(tǒng)內(nèi)存不足的時候导帝,out_of_memory()被觸發(fā)守谓,然后調(diào)用select_bad_process()選擇一個”bad”進程殺掉。如何判斷和選擇一個”bad進程呢您单?linux選擇”bad”進程是通過調(diào)用oom_badness()斋荞,挑選的算法和想法都很簡單很樸實:最bad的那個進程就是那個最占用內(nèi)存的進程。
具體OOM的解釋可以看這篇文章:http://www.wowotech.net/memory_management/oom.html
3睹限、oom_dump_tasks
當系統(tǒng)的內(nèi)存出現(xiàn)OOM狀況譬猫,無論是panic還是啟動OOM killer,做為系統(tǒng)管理員羡疗,你都是想保留下線索染服,找到OOM的root cause,例如dump系統(tǒng)中所有的用戶空間進程關(guān)于內(nèi)存方面的一些信息叨恨,包括:進程標識信息柳刮、該進程使用的total virtual memory信息、該進程實際使用物理內(nèi)存(我們又稱之為RSS,Resident Set Size秉颗,不僅僅是自己程序使用的物理內(nèi)存痢毒,也包含共享庫占用的內(nèi)存),該進程的頁表信息等等蚕甥。拿到這些信息后哪替,有助于了解現(xiàn)象(出現(xiàn)OOM)之后的真相。
當設(shè)定為0的時候菇怀,上一段描述的各種進程們的內(nèi)存信息都不會打印出來凭舶。在大型的系統(tǒng)中,有幾千個進程爱沟,逐一打印每一個task的內(nèi)存信息有可能會導(dǎo)致性能問題(要知道當時已經(jīng)是OOM了)帅霜。當設(shè)定為非0值的時候,在下面三種情況會調(diào)用dump_tasks來打印系統(tǒng)中所有task的內(nèi)存狀況:
(1)由于OOM導(dǎo)致kernel panic
(2)沒有找到適合的“bad”process
(3)找適合的并將其干掉的時候
4呼伸、oom_adj身冀、oom_score_adj和oom_score
準確的說這幾個參數(shù)都是和具體進程相關(guān)的,因此它們位于/proc/xxx/目錄下(xxx是進程ID)括享。假設(shè)我們選擇在出現(xiàn)OOM狀況的時候殺死進程搂根,那么一個很自然的問題就浮現(xiàn)出來:到底干掉哪一個呢?內(nèi)核的算法倒是非常簡單铃辖,那就是打分(oom_score兄墅,注意,該參數(shù)是read only的)澳叉,找到分數(shù)最高的就OK了隙咸。那么怎么來算分數(shù)呢?可以參考內(nèi)核中的oom_badness函數(shù):
![image.png](https://upload-images.jianshu.io/upload_images/22672059-ddbe03c8e30ab46c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
(1)對某一個task進行打分(oom_score)主要有兩部分組成成洗,一部分是系統(tǒng)打分五督,主要是根據(jù)該task的內(nèi)存使用情況。另外一部分是用戶打分瓶殃,也就是oom_score_adj了充包,該task的實際得分需要綜合考慮兩方面的打分。如果用戶將該task的 oom_score_adj設(shè)定成OOM_SCORE_ADJ_MIN(-1000)的話遥椿,那么實際上就是禁止了OOM killer殺死該進程基矮。
(2)這里返回了0也就是告知OOM killer,該進程是“good process”冠场,不要干掉它家浇。后面我們可以看到,實際計算分數(shù)的時候最低分是1分碴裙。
(3)前面說過了钢悲,系統(tǒng)打分就是看物理內(nèi)存消耗量点额,主要是三部分,RSS部分莺琳,swap file或者swap device上占用的內(nèi)存情況以及頁表占用的內(nèi)存情況还棱。
(4)root進程有3%的內(nèi)存使用特權(quán),因此這里要減去那些內(nèi)存使用量惭等。
(5)用戶可以調(diào)整oom_score珍手,具體如何操作呢?oom_score_adj的取值范圍是-1000~1000辞做,0表示用戶不調(diào)整oom_score珠十,負值表示要在實際打分值上減去一個折扣,正值表示要懲罰該task凭豪,也就是增加該進程的oom_score。在實際操作中晒杈,需要根據(jù)本次內(nèi)存分配時候可分配內(nèi)存來計算(如果沒有內(nèi)存分配約束嫂伞,那么就是系統(tǒng)中的所有可用內(nèi)存,如果系統(tǒng)支持cpuset拯钻,那么這里的可分配內(nèi)存就是該cpuset的實際額度值)帖努。oom_badness函數(shù)有一個傳入?yún)?shù)totalpages,該參數(shù)就是當時的可分配的內(nèi)存上限值粪般。實際的分數(shù)值(points)要根據(jù)oom_score_adj進行調(diào)整拼余,例如如果oom_score_adj設(shè)定-500,那么表示實際分數(shù)要打五折(基數(shù)是totalpages)亩歹,也就是說該任務(wù)實際使用的內(nèi)存要減去可分配的內(nèi)存上限值的一半匙监。
了解了oom_score_adj和oom_score之后,應(yīng)該是塵埃落定了小作,oom_adj是一個舊的接口參數(shù)亭姥,其功能類似oom_score_adj,為了兼容顾稀,目前仍然保留這個參數(shù)达罗,當操作這個參數(shù)的時候,kernel實際上是會換算成oom_score_adj静秆,有興趣的同學可以自行了解粮揉,這里不再細述了。
設(shè)置 進程 oom_score_adj 為負數(shù)來避免被kill
可以設(shè)置 -200
-1000代表完全禁止被kill
1
sudo echo 1000> /proc/< pid>/oom_score_adj
直接設(shè)置
是不可靠的,因為目標程序已在運行,在這種情況下,目標程序可能在之前導(dǎo)致OOM2 oom_score_adj在fork上繼承,因此您可以通過在父進程上設(shè)置所需的值來為新子項設(shè)置其初始值. 因此,如果您從shell腳本啟動目標, echo 1000 > /proc/$$/oom_score_adj 將shell的值更改為1000,隨后由shell分叉的任何進程都將以oom_score_adj設(shè)置
3 可以直接給bash設(shè)置抚笔,任何此終端啟動的進程都會繼承該設(shè)置
清理緩存空間
sudo su
echo 1 > /proc/sys/vm/drop_caches
echo 2 > /proc/sys/vm/drop_caches
echo 3 > /proc/sys/vm/drop_caches
有時是因為緩存占用了過多內(nèi)存扶认,導(dǎo)致OOM