編碼的世界 / 優質文選 / 生物

linux 內存占用過大分析


2022年1月11日
-   

1、使用 free -g 查看內存使用情況:

2、查看進程占用的內存情況:
ps aux|awk '{sum+=$6} END {print sum/1024}'
結果發現占用了 17G
3、查看內存分配情況:

cat /proc/meminfo





那slab是什麼呢?slab是Linux操作系統的一種內存分配機制。其工作是針對一些經常分配並釋放的對象,如進程描述符等,這些對象的大小一般比較小,如果直接采用夥伴系統來進行分配和釋放,不僅會造成大量的內碎片,而且處理速度也太慢。而slab分配器是基於對象進行管理的,相同類型的對象歸為一類(如進程描述符就是一類),每當要申請這樣一個對象,slab分配器就從一個slab列表中分配一個這樣大小的單元出去,而當要釋放時,將其重新保存在該列表中,而不是直接返回給夥伴系統,從而避免這些內碎片。slab分配器並不丟棄已分配的對象,而是釋放並把它們保存在內存中。當以後又要請求新的對象時,就可以從內存直接獲取而不用重複初始化。
具體為:
采用夥伴算法分配內存時,每次至少分配一個頁面。但當請求分配的內存大小為幾十個字節或幾百個字節時應該如何處理?如何在一個頁面中分配小的內存區,小內存區的分配所產生的內碎片又如何解決?
Linux2.0采用的解決辦法是建立了13個空閑區鏈表,它們的大小從32字節到132056字節。從Linux2.2開始,MM的開發者采用了一種叫做slab的分配模式,該模式早在1994年就被開發出來,用於Sun Microsystem Solaris 2.4操作系統中。Slab的提出主要是基於以下考慮:
1) 內核對內存區的分配取決於所存放數據的類型。例如,當給用戶態進程分配頁面時,內核調用get free page()函數,並用0填充這個頁面。 而給內核的數據結構分配頁面時,事情沒有這麼簡單,例如,要對數據結構所在的內存進行初始化、在不用時要收回它們所占用的內存。因此,Slab中引入了對象這個概念,所謂對象就是存放一組數據結構的內存區,其方法就是構造或析構函數,構造函數用於初始化數據結構所在的內存區,而析構函數收回相應的內存區。但為了便於理解,你也可以把對象直接看作內核的數據結構。為了避免重複初始化對象,Slab分配模式並不丟棄已分配的對象,而是釋放但把它們依然保留在內存中。當以後又要請求分配同一對象時,就可以從內存獲取而不用進行初始化,這是在Solaris 中引入Slab的基本思想。
實際上,Linux中對Slab分配模式有所改進,它對內存區的處理並不需要進行初始化或回收。出於效率的考慮,Linux並不調用對象的構造或析構函數,而是把指向這兩個函數的指針都置為空。Linux中引入Slab的主要目的是為了減少對夥伴算法的調用次數。
2) 實際上,內核經常反複使用某一內存區。例如,只要內核創建一個新的進程,就要為該進程相關的數據結構(task_struct、打開文件對象等)分配內存區。當進程結束時,收回這些內存區。因為進程的創建和撤銷非常頻繁,因此,Linux的早期版本把大量的時間花費在反複分配或回收這些內存區上。從Linux2.2開始,把那些頻繁使用的頁面保存在高速緩存中並重新使用。
3) 可以根據對內存區的使用頻率來對它分類。對於預期頻繁使用的內存區,可以創建一組特定大小的專用緩沖區進行處理,以避免內碎片的產生。對於較少使用的內存區,可以創建一組通用緩沖區(如Linux2.0中所使用的2的冪次方)來處理,即使這種處理模式產生碎片,也對整個系統的性能影響不大。
4) 硬件高速緩存的使用,又為盡量減少對夥伴算法的調用提供了另一個理由,因為對夥伴算法的每次調用都會“弄髒”硬件高速緩存,因此,這就增加了對內存的平均訪問次數。
Slab分配模式把對象分組放進緩沖區(盡管英文中使用了Cache這個詞,但實際上指的是內存中的區域,而不是指硬件高速緩存)。因為緩沖區的組織和管理與硬件高速緩存的命中率密切相關,因此,Slab緩沖區並非由各個對象直接構成,而是由一連串的“大塊(Slab)”構成,而每個大塊中則包含了若乾個同種類型的對象,這些對象或已被分配,或空閑,如圖6.12所示。一般而言,對象分兩種,一種是大對象,一種是小對象。所謂小對象,是指在一個頁面中可以容納下好幾個對象的那種。例如,一個inode結構大約占300多個字節,因此,一個頁面中可以容納8個以上的inode結構,因此,inode結構就為小對象。Linux內核中把小於512字節的對象叫做小對象。實際上,緩沖區就是主存中的一片區域,把這片區域劃分為多個塊,每塊就是一個Slab,每個Slab由一個或多個頁面組成,每個Slab中存放的就是對象。
2,通過slabtop命令查看slab緩存信息

通過slabtop我們看到Linux系統中有大量的dentry cache占用內存,那dentry cache是什麼呢?
首先,我們知道inode對應於物理磁盤上的具體對象,而dentry是一個內存實體,其中的d inode成員指向對應的inode,故可以把dentry看成是Linux文件系統中某個索引節點(inode)的鏈接,這個索引節點可以是文件,也可以是目錄。而dentry cache是目錄項高速緩存,是Linux為了提高目錄項對象的處理效率而設計的,它記錄了目錄項到inode的映射關系。
3,結合storm服務分析
當前服務器是storm集群的節點,首先想到了storm相關的工作進程,strace一下storm的worker進程發現其中有非常頻繁的stat系統調用發生,而且stat的文件總是新的文件名:
[@storm-yd8325 ~]# strace -fp 31984 -e trace=stat

進一步觀察到storm的worker進程會在本地目錄下頻繁的創建、打開、關閉、刪除心跳文件,每秒鐘一個新的文件名:
[@storm-yd8325 ~]# sudo strace -fp 31984 -e trace=open,stat,close,unlink

總結:storm進程頻繁的文件io操作,導致了dentry_cache占用了系統太多的內存資源。
4,系統的自動slab緩存回收
在slab緩存中,對象分為SReclaimable(可回收)和SUnreclaim(不可回收),而在系統中絕大多數對象都是可回收的。內核有一個參數,當系統內存使用到一定量的時候,會自動觸動回收操作。內核參數:
vm.min_free_kbytes = 836787

1)代表系統所保留空閑內存的最低限。
在系統初始化時會根據內存大小計算一個默認值,計算規則是:
min_free_kbytes = sqrt(lowmem_kbytes * 16) = 4 * sqrt(lowmem_kbytes)(注:lowmem_kbytes即可認為是系統內存大小)

另外,計算出來的值有最小最大限制,最小為128K,最大為64M。
可以看出,min free kbytes隨著系統內存的增大不是線性增長,因為隨著內存的增大,沒有必要也線性的預留出過多的內存,能保證緊急時刻的使用量便足矣。
2)min free kbytes的主要用途是計算影響內存回收的三個參數 watermark[min/low/high]
(1) watermark[high] > watermark [low] > watermark[min],各個zone各一套
(2)在系統空閑內存低於 watermark[low]時,開始啟動內核線程kswapd進行內存回收(每個zone一個),直到該zone的空閑內存數量達到watermark[high]後停止回收。如果上層申請內存的速度太快,導致空閑內存降至watermark[min]後,內核就會進行direct reclaim(直接回收),即直接在應用程序的進程上下文中進行回收,再用回收上來的空閑頁滿足內存申請,因此實際會阻塞應用程序,帶來一定的響應延遲,而且可能會觸發系統OOM。這是因為watermark[min]以下的內存屬於系統的自留內存,用以滿足特殊使用,所以不會給用戶態的普通申請來用。
(3)三個watermark的計算方法:
watermark[min] = min free kbytes換算為page單位即可,假設為min free pages。(因為是每個zone各有一套watermark參數,實際計算效果是根據各個zone大小所占內存總大小的比例,而算出來的per zone min free pages)
 watermark[low] = watermark[min] * 5 / 4
watermark[high] = watermark[min] * 3 / 2

所以中間的buffer量為 high - low = low - min = per_zone_min_free_pages * 1/4 。因為 min_free_kbytes
= 4* sqrt(lowmem_kbytes)
 ,也可以看出中間的buffer量也是跟內存的增長速度成開方關系。
(4)可以通過/proc/zoneinfo查看每個zone的watermark
例如:
Node 0, zone DMA
pages free 3960
min 65
low 81
high 97

3)min free kbytes大小的影響
min_free_kbytes 設的越大,watermark的線越高,同時三個線之間的buffer量也相應會增加。這意味著會較早的啟動kswapd進行回收,且會回收上來較多的內存(直至watermark[high]才會停止),這會使得系統預留過多的空閑內存,從而在一定程度上降低了應用程序可使用的內存量。極端情況下設置 min_free_kbytes 接近內存大小時,留給應用程序的內存就會太少而可能會頻繁地導致OOM的發生。
min_free_kbytes 設的過小,則會導致系統預留內存過小。kswapd回收的過程中也會有少量的內存分配行為(會設上PF_MEMALLOC)標志,這個標志會允許kswapd使用預留內存;另外一種情況是被OOM選中殺死的進程在退出過程中,如果需要申請內存也可以使用預留部分。這兩種情況下讓他們使用預留內存可以避免系統進入deadlock狀態。
5,最終結果
最終查明, slab cache 占用過多屬於正常問題,並且當內存到達系統最低空閑內存限制的話,會自動觸發kswapd進程來回收內存,屬於正常現象。
注:測了一下,當調整完min free kbytes值大於系統空閑內存後,kswapd進程的確從休眠狀態進入運行態,開始回收內存。
根據我們跑的storm服務做出如下調整:
vm.vfs_cache_pressure = 200

該文件表示內核回收用於directory和inode cache內存的傾向;缺省值100表示內核將根據pagecache和swapcache,把directory和inode cache保持在一個合理的百分比;降低該值低於100,將導致內核傾向於保留directory和inode cache;增加該值超過100,將導致內核傾向於回收directory和inode cache。

熱門文章