一、 問題現象
客戶收到系統告警,K8S 集群某些節點 used 內存持續升高,top 查看進程使用的內存并不多,剩余內存不足卻找不到內存的使用者,內存神秘消失,需要排查內存去哪兒了。
執行 top 指令并按內存排序輸出,內存使用最多的進程才 800M 左右,加起來遠達不到 used 9G 的使用量。
二、問題分析
2.1 內存去哪兒了?
在分析具體問題前,我們先把系統內存分類,便于找到內存使用異常的地方,從內存使用性質上,可以簡單把內存分為應用內存和內核內存,兩種內存使用量加上空閑內存,應該接近于 memory total,這樣區分能夠快速定位問題的邊界。
其中 allocpage 指通過 __get_free_pages/alloc_pages 等 API 接口直接從伙伴系統申請的內存量(不包含 slab 和 vmalloc)。
2.1.1 內存分析
根據內存大圖分別計算應用內存和內核內存,就可以知道是哪部分存在異常,但這些指標計算比較繁瑣,很多內存值還存在重疊。針對這個痛點,SysOM 運維平臺的內存大盤功能以可視化的方式展示內存的使用情況,并直接給出內存是否存在泄漏,本案例中,使用 SysOM 檢測,直接顯示 allocpage 存在泄漏,使用量接近 6G。
2.1.2 allocpage 內存
那既然是 alloc page 類型的內存占用多,是否可以直接從 sysfs、procfs 文件節點查看其內存使用了?很遺憾,這部分內存是內核/驅動直接調用 __get_free_page/alloc_pages 等函數從伙伴系統申請單個或多個連續的頁面,系統層面沒有接口查詢這部分內存使用詳情。如果這類內存存在泄漏,就會出現"內存憑空消失"的現象,比較難發現,問題原因也難排查。針對這個難點,我們的SysOM系統運維能夠覆蓋這類內存統計和原因診斷。
所以需要進一步通過SysOM的診斷利器 SysAK 動態抓取這類內存的使用情況。
2.2 allocPage 類型內存排查
2.2.1 動態診斷
對于內核內存泄漏,我們直接可以使用SysAK工具來動態追蹤,啟動命令并等待 10 分鐘。
sysak memleak -t page -i 600
診斷結果顯示 10 分鐘內 receive_mergeable 函數分配的內存有 4919 次沒有釋放,內存大小在 300M 左右,分析到這里,我們就需要結合代碼來確認 receive_mergeable 函數的內存分配和釋放邏輯是否正確。
2.2.2 分配和釋放總結
1)page_to_skb 每次會分配一個線性數據區為 128 Byte 的 skb。
2)數據區調用 alloc_pages_node 函數,一次性從伙伴系統申請 32k 內存(order=3)。
3)每個 skb 會對 32k 的 head page 產生一次引用計數,也就是只有當所有 skb 都釋放時,這 32k 內存才釋放回伙伴系統。
4)receive_mergeable 函數負責申請內存,但不負責釋放這部分內存,只有當應用從 socket recvQ 中把數據讀走才會對 head page 引用計數減一,當 page refs 為 0 時,釋放回伙伴系統。
當應用消費數據比較慢,可能會導致 receive_mergeable 函數申請的內存釋放不及時,而且最壞情況一個 skb 會占用 32k 內存,使用 sysak skcheck 檢查 socket 接收隊列和發送隊列殘留情況。
從輸出可以知道,系統中只有 nginx 進程的接收隊列有殘留數據,socket fd=11 的 Recv-Q 有接近 3M 的數據沒有接收,通過直接 kill 146935,系統內存恢復正常了,所以問題根本原因就是 nginx 沒有及時收走數據了。
三、問題結論
經過與業務方溝通,最終確認是業務配置問題,導致 nginx 有一個線程沒有處理數據,從而導致網卡驅動申請的內存沒有及時釋放,而 allocpage 內存又是無法統計的,從而出現內存憑空消失的現象。
3.1 結論驗證
接收隊列真的有數據殘留嗎,這里結合 crash 工具的 files 指令通過 fd 找到對應的sock:
通過多次觀察,發現 sk_receive_queue 上的 skb 長時間沒有變化,這也證明了 nginx 沒有及時處理接收隊列上的 skb,導致在網卡驅動中分配的內存沒有釋放。
四、內存泄漏疑點
在排查過程還遇到一個非常較困惑的地方,sockstat 和 slabtop 看檢查 tcp mem 和 skbuff_head_cache 使用都很正常,導致進一步掩蓋了網絡占用的內存。
tcp mem = 32204*4K=125M
skb 數量在 1.5萬~3 萬之間。
按照前面分析,一個skb最壞情況占用 32k 內存,那么 2 萬個 skb 最大也就占 600M 左右,怎么會占用幾個 G 了,難道分析有問題?如下圖所示,skb 的非線性區可能還存在若干個 frag page,而每個 frag page 又可能由 compund page 組成。
用 crash 實際讀取 skb 內存發現,有些 skb 存在 17 個 frag page,并且數據大小只有 10 Byte。
解析 frag page 的 order 為 3,意味著一個 frag page 占用 32k 內存。
極端情況下,一個 skb 可能占用(1+17)*8=144 頁,上圖 slabinfo 中skbuff_head_cache 活躍 object 數量為 15033 個,所以理論最大總內存 =144*15033*4K = 8.2G,而我們現在遇到的場景消耗 6G 的內存是完全有可能的。
審核編輯:劉清
-
接口
+關注
關注
33文章
8950瀏覽量
153221 -
驅動
+關注
關注
12文章
1900瀏覽量
86539 -
內存泄漏
+關注
關注
0文章
40瀏覽量
9351
發布評論請先 登錄
為什么打印函數rt_kprintf("");會多一個空行?
教你如何搭建淺層神經網絡"Hello world"
幾種IO口模擬串口"硬核"操作

"STM32F0 Error: Flash Download failed - ""Cortex-M0""解決"

喜訊 | 凌科電氣榮獲國家級專精特新&amp;quot;小巨人&amp;quot;企業

芯片工藝的&quot;7nm&quot; 、&quot;5nm&quot;到底指什么?

程控交流電源開機顯示&quot;ERR&quot;的原因及解決方法分析
科沃斯掃地機器人通過TüV萊茵&quot;防纏繞&quot;和&quot;高效邊角清潔&quot;認證
3分鐘讀懂 | 氣密性檢測儀如何&amp;quot;揪出&amp;quot;泄漏點?原理全拆解!

【硬核測評】凌華DAQE雙雄爭霸:工業數據采集界的&amp;quot;速度與激情&amp;quot;實戰解析

隧道管廊變壓器局放在線監測:為地下&amp;quot;電力心臟&amp;quot;裝上智能聽診器

電纜局部放電在線監測:守護電網安全的&amp;quot;黑科技&amp;quot;

倉儲界的&quot;速效救心丸&quot;,Ethercat轉PROFINET網關實戰案例

評論