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