河南 劉進(jìn)京
單位的一臺服務(wù)器出現(xiàn)故障,導(dǎo)致正常的業(yè)務(wù)無法進(jìn)行。該服務(wù)器主要進(jìn)行市場數(shù)據(jù)評估分析,分析程序使用Java 開發(fā)。該服務(wù)器一直運(yùn)行比較正常,使用的是CentOS 6.X系統(tǒng),因?yàn)榻鼛兹諗?shù)據(jù)量大增的緣故,才出現(xiàn)“罷工”問題。
登錄到該服務(wù)器上,執(zhí)行“dmesg”命令,查看系統(tǒng)運(yùn)行信息。在其中發(fā)現(xiàn)“Out of memory:Kill process 31076(Java)”,“oom_adj=0,oom_score_adj=0”等內(nèi)容,說明Java 進(jìn)程被系統(tǒng)終結(jié),造成該分析系統(tǒng)無法正常運(yùn)作的問題。
根據(jù)提示信息中的“Out of memory”字樣,說明這是因?yàn)镴ava 進(jìn)程大量申請系統(tǒng)內(nèi)存資源,導(dǎo)致內(nèi)存溢出,才被系統(tǒng)自動關(guān)閉。
我 們知道,在Linux 內(nèi)核中存在“Out of Memory killer”管理機(jī)制,當(dāng)系統(tǒng)內(nèi)存低于警戒線時(shí),該管理機(jī)制就會被自動激活,系統(tǒng)會根據(jù)實(shí)際情況,挑選并關(guān)閉合適的進(jìn)程,來釋放更多的內(nèi)存,保證系統(tǒng)可以正常運(yùn)行。
執(zhí)行“free -m”命令,查看內(nèi)存使用情況,發(fā)現(xiàn)該機(jī)總內(nèi)存為32GB,但是可用內(nèi)存卻不足500MB,而且Cache和Buffer 的數(shù)值都比較低,說明系統(tǒng)內(nèi)存消耗的很大,可能因?yàn)樵摲治鱿到y(tǒng)使用的內(nèi)存量比較高,所以系統(tǒng)就將對應(yīng)的Java 進(jìn)程關(guān)閉。
“Out of Memory killer”機(jī)制是系統(tǒng)在內(nèi)存不足的情況下采取的應(yīng)對策略,該機(jī)制會使用一套啟發(fā)式算法,來計(jì)算所有進(jìn)程的分?jǐn)?shù),對于得到最高的進(jìn)程來說,其占用的內(nèi)存量就最高,系統(tǒng)就會將其關(guān)閉。
在正常情況下,系統(tǒng)內(nèi)核會根據(jù)應(yīng)用程序的要求來按需分配內(nèi)容,但是不同的應(yīng)用程序并不會將分配的內(nèi)存全部用完。對于這些“剩余”的內(nèi)存來說,系統(tǒng)可以對其合理利用。但是這些內(nèi)存量屬于不同的進(jìn)程,系統(tǒng)如果直接回收的話,實(shí)現(xiàn)起來是比較繁瑣的。
為此系統(tǒng)會采用“Over-Commit memory”(即過度分配內(nèi)存)的方法,來提高內(nèi)存的整體使用率。即系統(tǒng)會判定各進(jìn)程不會同時(shí)使用并用完分配給它們的內(nèi)存上限。在一般情況下,系統(tǒng)這樣管理內(nèi)存是沒有問題的。
但是,如果大多數(shù)應(yīng)用程序都用完了系統(tǒng)分配給其的內(nèi)存后,就會造成這些內(nèi)存消耗量之和大于物理內(nèi)存容量的情況,進(jìn)而就會出現(xiàn)系統(tǒng)可用內(nèi)存急速降低甚至不堪使用的情況,即沒有內(nèi)存頁能夠分配給進(jìn)程。
而系統(tǒng)為了保證正常運(yùn)作,必須停止一些消耗內(nèi)存的“大戶”,才可以釋放出更多的內(nèi)存。這樣就必然激活“Out of Memory killer”機(jī)制,利用對應(yīng)的啟發(fā)式算法,來關(guān)閉合適的進(jìn)程。
在本例中,因?yàn)镴ave 進(jìn)程占用的內(nèi)存量比較大,自然成了管控的目標(biāo)。對于“Out of Memory killer”機(jī)制來說,其實(shí)際上是根據(jù)不同進(jìn)程的打分情況,來決定其是否可以被關(guān)閉。對于Root 級別的進(jìn)程來說,因?yàn)槠涞匚槐容^重要,所以一般不會被輕易關(guān)閉,在評分上會得到3%的“優(yōu)待”。用戶空間的進(jìn)程可以對和其關(guān)聯(lián)的“oom_adj”內(nèi)核參數(shù),來調(diào)整得分情況,降低其被系統(tǒng)關(guān)閉的可能性。
例如對于上述Java 進(jìn)程來說,可以執(zhí)行“cat/proc/31076/oom_score”命令,來顯示顯示其當(dāng)前的得分?jǐn)?shù)。執(zhí)行“echo -10 >/proc/31076/oom_score_adj”命令,可以調(diào)整與其對應(yīng)的“oom_score_adj”參數(shù),來降低評分值,這里將“oom_score_adj”參數(shù)的值設(shè)置為“-10”,您可以根據(jù)根據(jù)實(shí)際情況進(jìn)行調(diào)整。之后再執(zhí)行“cat/proc/31076/oom_score”命令,可以看到其評分降低了,這樣就降低了其被系統(tǒng)關(guān)閉的概率。
根據(jù)以上分析,可以看到正是因?yàn)榻鼇頂?shù)據(jù)量的激增,導(dǎo)致該分析系統(tǒng)消耗的內(nèi)存量很大,遠(yuǎn)超平時(shí)的內(nèi)存使用量,大量的占用可用內(nèi)存,才被系統(tǒng)使用“Out of Memory killer”機(jī)制關(guān)閉。
執(zhí) 行“cat/proc/sys/overcommit_memory/”命令,發(fā)現(xiàn)其數(shù)值為1,對于“overcommit_memory”參數(shù)來說,可以對“Out of Memory killer”機(jī)制進(jìn)行控制,如果將其值設(shè)置為“0”,表示在用戶申請內(nèi)存時(shí),系統(tǒng)會對剩余內(nèi)存進(jìn)行檢測,如果發(fā)現(xiàn)可用內(nèi)存無法滿足要求,就會申請失敗。
如果將其值設(shè)置為“1”,表示在用戶申請內(nèi)存時(shí),系統(tǒng)不進(jìn)行可用內(nèi)存量的檢測,直到使用的內(nèi)存超過可用內(nèi)存為止。如果將其值設(shè)置為“2”,表示當(dāng)用戶一次申請的內(nèi)存大小不允許超過可用內(nèi)存值。這里為保證系統(tǒng)正常運(yùn)行,最好將其設(shè)置為“0”。執(zhí)行“sysctl -w vm.overcommit_memory=0”,“echo " vm.overcommit_memory=0">>/etc/sysctl.conf”命令,將該參數(shù)的值設(shè)置為“0”。這樣可以最大限度的避免可用內(nèi)存過低,導(dǎo)致系統(tǒng)自行關(guān)閉目標(biāo)進(jìn)程的問題。
可以看出,該故障就是因?yàn)閮?nèi)存問題引發(fā)。為了保證該服務(wù)器順暢運(yùn)行,很有必須對其內(nèi)存使用情況進(jìn)行合理優(yōu)化。
例如在執(zhí)行“free -m”命令時(shí),會看到“cache”和“buffers”參數(shù),這兩者其實(shí)都是系統(tǒng)用來保存曾經(jīng)打開的文件信息之用,當(dāng)系統(tǒng)需要讀取對應(yīng)的文件時(shí),會從“cached”和“buffers”內(nèi)存區(qū)域中查找。如果可以找到的話,就直接將數(shù)據(jù)讀出,發(fā)送給目標(biāo)程序,如果沒有的話,才從磁盤中讀取數(shù)據(jù)。利用系統(tǒng)的緩存機(jī)制,可以有效提高讀寫性能。
對 于Buffers 來 說,表示塊設(shè)備所占用的緩存頁,這包括直接讀取塊設(shè)備、文件系統(tǒng)元數(shù)據(jù)等使用的緩存頁。Cached 表示普通文件所占用的緩存頁,其將讀取過的數(shù)據(jù)保存起來,在重新讀取時(shí)如果發(fā)現(xiàn)需要的數(shù)據(jù),將不需要重新讀取磁盤。對于Cached 來說,會將緩存的數(shù)據(jù)按照讀取的頻度進(jìn)來管理,將最頻繁讀取的數(shù)據(jù)保存到易于找到的位置,將不容易讀取的內(nèi)容排列在后面,對于最不經(jīng)常讀取的數(shù)據(jù)則進(jìn)行刪除。對于Linux來說,提供了自動釋放Cache的機(jī)制,但是為了提高靈活性,可以手動處理。
例如執(zhí)行以下命令:

可以分別釋放Page Cache,釋放文件節(jié)點(diǎn)緩存和目錄項(xiàng)緩存,釋放Page cache、dentries 和nodes 緩存。
注意,在手動釋放之前,可以執(zhí)行“sync”命令,將所有未寫的系統(tǒng)緩沖區(qū)寫到磁盤中,其中包含已修改的i-node、已延遲的塊 I/O 和讀寫映射文件。Linux 使用的基于分頁存取的內(nèi)存管理機(jī)制為了充分利用物理內(nèi)存,系統(tǒng)就會根據(jù)情況將物理內(nèi)存中不常用的數(shù)據(jù)塊自動交換到SWAP 虛擬內(nèi)存之中。
在系統(tǒng)內(nèi)核參數(shù)中,“swappiness”的值掌控使用SWAP 分區(qū)的比例。執(zhí)行“cat/proc/sys/ym/swappiness”命令,可以查看該參數(shù)的值,其默認(rèn)為60,表示內(nèi)存使用到40%的時(shí)候,才開始使用虛擬內(nèi)存。
為了盡可能的使用內(nèi)存,可以將它修改為合適的值,例如,執(zhí)行“sysctl vm.swappiness=20”命令,將其設(shè)置為20,這樣的話,只有當(dāng)內(nèi)存使用到80%時(shí),才可以開始使用虛擬內(nèi)存等。而如果想永久保存修改的話,則可以打開“/etc/sysctl.conf”文件,并在其中添加“vm.swappiness=x”行即可,其中的“x”表示的是具體的數(shù)值。
對于Linux 文件系統(tǒng)來說,會使用Page cache 來優(yōu)化文件讀寫操作。對于讀寫的數(shù)據(jù)都緩存到Page Cahce中,在執(zhí)行上述“free -m”命令時(shí),在“Cached”中顯示的就是該緩存信息。在讀文件時(shí),會現(xiàn)在Page Cache 中查找,如果找到就直接讀取,否則的話再從磁盤讀取文件,并寫入到Page Cahce 中。在寫入數(shù)據(jù)時(shí),會先將其寫入到Page Cache,并打上“dirty”標(biāo)記,寫入緩存中的數(shù)據(jù)會定期批量保存到文件系統(tǒng)中。
對于Page Cache 來說,系統(tǒng)內(nèi)核參數(shù)“vm.dirty_background_ratio”的作用是,當(dāng)緩存中的帶有“Dirty”標(biāo)記的頁的數(shù)量達(dá)到了系統(tǒng)內(nèi)存的某個(gè)比率時(shí),系統(tǒng)才將緩存的數(shù)據(jù)頁異步寫入磁盤。
例如執(zhí)行“sysctl -w vm.dirty_background_ratio=5”命令,將其設(shè)置為5%,就是一個(gè)比較合理的數(shù)值。內(nèi)核參數(shù)“vm.dirty_ratio”的作用是當(dāng)緩存的臟頁數(shù)量達(dá)到系統(tǒng)內(nèi)存某個(gè)比率時(shí),系統(tǒng)必須將緩存臟頁刷入磁盤,這主要是為了避免丟失數(shù)據(jù)。執(zhí)行“sysctl-w vm.dirty_ ratio=10”命令,將該比率設(shè)置為10%,則是比較合理的數(shù)值。
如果該值過大,就會很容易造成當(dāng)系統(tǒng)將臟頁緩存刷入磁盤時(shí),引發(fā)其他程序在讀寫數(shù)據(jù)時(shí)觸發(fā)I/O 阻塞的情況。