1. 程式人生 > 其它 >記憶體洩漏排查

記憶體洩漏排查

pmap 命令檢視記憶體分佈:https://blog.csdn.net/weixin_39639119/article/details/85704569

查現象
這裡先入為主,根據提供的資訊是使用者態佔用差不多,所以走了很多彎路,查了很久的核心佔用。
由於核心是存在空洞的,因此無法確定具體哪些模組使用了哪些記憶體。因此重新著手分析全部的記憶體佔

比較/proc/meminfo
對比兩臺裝置的/proc/meminfo資訊:10.222.3.18的使用者態程序使用了2187M;10.222.3.49使用者
態程序1897M程序,相差了300M。所以問題出現在使用者態。
(這裡需要說明的是 Active + Inactive就是使用者態使用記憶體的綜合,感興趣的可以去了解下核心的內
存管理)
(10.222.3.18)
(10.222.3.49)
比較RSS
雖然說rss並不能夠完全準確的代表程序使用的實體記憶體大小,但是能夠看出些端倪,我們能夠比
較相同程序的佔用來確定是否有程序佔用差異很大。
通過ps獲取rss跟程序的對應關係,比較佔用較大的一些程序;可以發現fwlog和logsd相加起來
10.222.3.18要比10.222.3.49多出約250M。
這裡鎖定了fwlog來檢視。

fwlog對比
依然是對比,這次我們對比兩個程序的記憶體空間,通過pmap -x pid的方式來對比,結果如下。
可以看到10.222.3.18比10.222.3.49多使用了約140M的匿名記憶體頁,而且這些匿名記憶體頁都是Dirty的
狀態,也就是說這些記憶體頁是正在被使用的,根據fwlog的功能分析,它不應該長時間持有這麼大的內
存,說明出現了記憶體洩漏。

分析洩漏記憶體的內容
我們可以用gdb來dump出想要的程序記憶體,例如0x7f21b8000000 這一塊,通過如下方式:
gdb: dump memory /fwlog/test_memory.dump 0x7f21b8000000 0x7F21BBFFF000
(這裡可以通過gdb -p進入後來執行命令,也可以通過編寫test_dump.gdb,然後gdb -q -x
test_dump.gdb -p pid來實現無互動的gdb,這個對於那些無法長時間中斷的程序除錯很有效。)

vim test_dump.gdb

dump memory /fwlog/test_memory.dump 0x7f21b8000000 0x7F21BBFFF000

通過hexdump來檢視dump出來的記憶體:可以看到裡面存的是session相關的字串,而且是json
格式的字串。fwlog中只有一個地方用到了session的json轉字串

分析fwlog程式碼
在fwlog/fwlog/write_to_thrid.cc中的write_to_third函式中。初次分析好像沒有什麼問題,
json_object_put會釋放掉json_str;深入看了以後還是證明這個json庫是沒有問題的,雖然寫的很復
雜。
在檢視json庫無果了以後,開始懷疑kafka傳送那裡可能拷貝一份json_str,因為它呼叫的是asyn
介面,一般來說是非同步的,也就是說在kafka處理前,這個json_str已經走完了釋放邏輯。

在kafka的實現中可以看到,它進行了produce以後就退出了,而且引數明確表示它拷貝了
payload。

當kafka 服務端異常時,客戶端可能出現msg的積累,積累數量可以在程式碼中找到,限制是10W條和1G,按照msg 大學自傲來算,最多累積約200M

除了fwlog程序,還有logsd和auditd都使用了kafka,這三個程序合起來比另外一臺主機上多使用
了300M記憶體。所以所謂的記憶體洩漏的原因是由於kafka服務端連不上導致,但是我們在編碼過程中應該
識別到這種風險,畢竟記憶體的增長可能導致OOM,在10.222.3.18上就出現了OOM殺程序的問題。
最後討論的結果是通過配置限制kafka快取的訊息數和訊息大小,來避免出現佔用過多記憶體的問
題。

在python開發中有我們可能會用到一些C庫的so裡面的功能,有些會使用ctype來進行操作,在使用的時候需要注意記憶體釋放問題:
1、ctype pointer的釋放問題
xxx
result = ctypes.pointer(xxx)
xxx
上面的程式碼中result轉換成為了ctype的指標,可以傳遞給so裡面的c函式作為引數,但是需要注意,不論是否進行其它操作,一旦呼叫了ctypes.pointer,這個result是會被快取起來的,
它並不會被主動釋放,即使result的生命週期結束。這裡必須在使用完後配合呼叫ctypes._reset_cache()來釋放空間

2、so裡面有記憶體申請
so裡面如果有malloc和mmap一類的記憶體申請,那麼需要so提供相應的釋放介面,否則記憶體也是無法被釋放的