當進程通過malloc申請虛擬內存后,操作系統(tǒng)不會立即為其分配物理內存,而是在首次訪問時,才觸發(fā)缺頁異常分配內存。對普通進程來說,能看到的是內核提供的虛擬內存,這些虛擬內存還需要通過頁表,由系統(tǒng)映射為物理內存。
為平衡CPU與磁盤間的性能差異,Linux會使用Cache把文件數(shù)據(jù)緩存到內存中。
內存分配
用戶空間內存包括多個不同類型的內存段,比如只讀段、數(shù)據(jù)段、堆、棧以及文件映射段等,下面逐個分析哪些地方可能出現(xiàn)內存泄漏。
只讀段,包括程序的代碼和常量,只讀段不會再去分配新的內存,因此不會產(chǎn)生內存泄漏。
數(shù)據(jù)段,包括全局變量和靜態(tài)變量,這些變量在定義時就已經(jīng)確定了大小,也不會產(chǎn)生內存泄漏。
棧內存由系統(tǒng)自動分配和管理,只會出現(xiàn)爆?;蛘卟葍却媲闆r,超出了局部變量的作用域時,棧內存會被系統(tǒng)自動回收,不會出現(xiàn)內存泄漏情況。
堆內存由應用程序自己來分配和管理,如果業(yè)務沒有正確釋放堆內存,就會造成內存泄漏,所以沒有特殊情況,盡量使用智能指針管理對象生命周期。
內存映射段,包括動態(tài)鏈接庫和共享內存,其中共享內存由程序動態(tài)分配和管理。所以如果程序在分配后忘了回收,就會導致內存泄漏。
對于內存泄漏,只能依賴系統(tǒng)OOM機制殺死進程,但是此時已經(jīng)造成了嚴重的性能問題,如系統(tǒng)的緩存頻繁回收等,會導致業(yè)務不可用。
系統(tǒng)命令
top?能觀察系統(tǒng)和進程的內存占用情況,vmstat能觀察內存的變化趨勢。
觀察free列,如果一直在減小,說明系統(tǒng)可用內存在變少??梢酝ㄟ^top或ps來觀察進程的內存使用情況,然后找出內存使用一直增長的進程,最后再通過pmap分析進程地址空間中內存的使用情況。
使用緩沖區(qū)分析工具cachetop分析這些緩存被哪些進程占用;free查看可用內存被哪些進程占用。
cachetop可以分析業(yè)務是否有繞過緩存直接讀取磁盤,導致讀寫速度慢;strace可以查看具體的系統(tǒng)調用:
strace -p $(pgrep)?PID
檢測工具
如果是編碼階段,在上線前檢查代碼中資源獲取的方式,盡量改為智能指針自動管理資源生命周期;禁止系統(tǒng)的swap機制,減少內存和硬盤的交換數(shù)據(jù)導致性能開銷。
如果是調試階段檢查內存泄漏,可以mock掉業(yè)務的malloc和free,在malloc/new時將調用方法、申請地址寫入指定文件新增一條記錄,free時根據(jù)地址刪除該條記錄,在程序運行結束后可以從文件中找到內存泄漏的地方。
bcc中的memleak就是類似思路,利用插樁統(tǒng)計分配的字節(jié)和釋放的字節(jié),兩者進行抵消,而沒有釋放的調用棧就是可能泄露的地方。
其他內存泄漏檢測工具:
AddressSanitizer:速度快,首選;
Valgrind:攔截內存分配和釋放函數(shù),無需修改代碼,但是速度比較慢;
mtrace:通過環(huán)境變量替換malloc和free函數(shù),方便使用。