Linux下內存統計和內存泄露等問題的確定方法

[導讀] 在產品的開發中,通過對當前系統消耗內存總量的統計,可以對產品所需內存總量進行精確的評估,從而選擇合適的內存芯片與大小,降低產品的成本。在遇到內存泄露類問題時,經常會對此束手無策,本文通過對proc下進

在產品的開發中,通過對當前系統消耗內存總量的統計,可以對產品所需內存總量進行精確的評估,從而選擇合適的內存芯片與大小,降低產品的成本。在遇到內存泄露類問題時,經常會對此束手無策,本文通過對proc下進程相關的文件進行分析,精確評估系統消耗內存的大小,還可以對內存泄露類問題的解決提供一種定位手段。

linux在內存使用上的原則是:如果內存充足,不用白不用,盡量使用內存來緩存一些文件,從而加快進程的運行速度,而當內存不足時,會通過相應的內存回收策略收回cache內存,供進程使用。

一、系統總內存的分析

可以從proc目錄下的meminfo文件了解到當前系統內存的使用情況匯總,其中可用的物理內存=memfree+buffers+cached,當memfree不夠時,內核會通過回寫機制(pdflush線程)把cached和buffered內存回寫到后備存儲器,從而釋放相關內存供進程使用,或者通過手動方式顯式釋放cache內存:

echo 3 > /proc/sys/vm/drop_caches

下圖是海思平臺下當前系統內存的總體使用情況,其中可以看到,系統消耗掉了29M的內存,下面繼續分析這些內存都是被誰消耗掉了。

# cat /proc/meminfo
?MemTotal: ? ? ? ?68956 kB

MemFree: ? ? ? ? 18632 kB

Buffers: ? ? ? ? ?4096 kB

Cached: ? ? ? ? ?17260 kB

SwapCached: ? ? ? ? ?0 kB

Active: ? ? ? ? ?21304 kB

Inactive: ? ? ? ?19248 kB

SwapTotal: ? ? ? ? ? 0 kB

SwapFree: ? ? ? ? ? ?0 kB

Dirty: ? ? ? ? ? ? ? 0 kB

Writeback: ? ? ? ? ? 0 kB

AnonPages: ? ? ? 19216 kB

Mapped: ? ? ? ? ? 2472 kB

Slab: ? ? ? ? ? ? 6900 kB

SReclaimable: ? ? ?924 kB

SUnreclaim: ? ? ? 5976 kB

PageTables: ? ? ? ?460 kB

NFS_Unstable: ? ? ? ?0 kB

Bounce: ? ? ? ? ? ? ?0 kB

CommitLimit: ? ? 62060 kB

Committed_AS: ? ?28864 kB

VmallocTotal: ? 442368 kB

VmallocUsed: ? ? 46984 kB

VmallocChunk: ? 393212 kB

  二、進程使用內存的統計

在32位操作系統中,每個進程擁有4G的虛擬內存空間,其中0~3GB是每個進程的私有用戶空間,這個空間對系統中其他進程是不可見的。3~4GB是linux內核空間,由系統所有的進程以及內核所共享的。通過訪問/proc/{pid}/下相關文件,可以了解每個線程虛擬內存空間的使用情況,從而了解每個線程所消耗內存的多少。

由于我們的產品都是使用多線程方式實現的,多個線程共享一個進程的用戶態虛擬地址空間,虛擬地址空間包含若干區域,主要有如下幾個區域:

1、當前執行文件的代碼段,該代碼段稱為text段。

2、執行文件的數據段,主要存儲執行文件用到的全局變量,靜態變量。

3、存儲全局變量和動態產生的數據的堆。

4、用于保存局部變量和實現函數調用的棧。

5、采用mmap方式映射到虛擬地址空間中的內存段

所以只需要查看任意一個線程的用戶態虛擬地址空間分配即可知道屬于同一進程的所有線程占用總內存的大小。可以通過查看/proc/{pid}/maps文件來獲取相關的虛擬地址空間內容,下文摘列部分典型的內容:

# cat /proc/568/maps
?00008000-0036a000 r-xp 00000000 00:0e 236 ? ? ? ?/home/hik/hicore

00372000-003a5000 rw-p 00362000 00:0e 236 ? ? ? ?/home/hik/hicore

003a5000-00e28000 rwxp 003a5000 00:00 0 ? ? ? ? ?[heap]

40000000-40005000 r-xp 00000000 01:00 94 ? ? ? ? /lib/ld-uClibc.so.0

416db000-41770000 rw-s c2005000 00:0f 68 ? ? ? ? /dev/mem

b51fc000-b5200000 rwxp b51fc000 00:00 0

…….

be1fc000-be200000 rwxp be1fc000 00:00 0

be93b000-be950000 rwxp befeb000 00:00 0 ? ? ? ? ?[stack]

第一行:從r-xp可知其權限為只讀、可執行,該段內存地址對應于執行文件的代碼段,程序的代碼段需加載到內存中才可以執行。由于其只讀,不會被修改,所以在整個系統內共享。

第二行:從rw-p可知其權限為可讀寫,不可執行,該段內存地址對應于執行文件的數據段,存放執行文件所用到的全局變量、靜態變量。

第三行:從rwxp可知其權限是可讀寫,可執行,地址空間向上增長,而且不對應文件,是堆段,進程使用malloc申請的內存放在堆段。每個進程只有一個堆段,不論是主進程,還是不同的線程申請的內存,都反映到到進程的堆段。堆段向上增長,最大可以增長到1GB的位置,即0x40000000,如果大于1GB,glibc將采用mmap的方式,為堆申請一塊內存。

第四行:是程序連接的共享庫的內存地址。

第五行:是以mmap方式映射的虛擬地址空間。

第六、七行:是線程的棧區地址段,每個線程的棧大小都是16K。

第八行:是進程的棧區。關于棧段,每個線程都有一個,如果進程中有多個線程,則包含多個棧段。

三、當前系統總內存的統計

1、進程占用的總內存可以通過上述maps表計算出來。

2、當系統運行起來以后,會把應用層相關的文件掛載到tmpfs文件系統下,海思系統下這部分大概有13M左右,這部分內存是以cache方式統計出來的,但是這部分內存cache無法通過回收策略或者顯式的調用釋放掉。

3、根文件系統ramdisk占用的內存。

4、當前系統保留內存的大小,可以通過查看/proc/sys/vm/min_free_kbytes來獲取或者修改此內存的大小。

5、當然,當系統運行起來后,還應該留有一定的內存用于在硬盤讀寫時做cache或者網絡負荷比較高時分配skb等,一般需要30M以上。

四、對調試內存泄露類問題的一些啟示

當進程申請內存時,實際上是glibc中內置的內存管理器接收了該請求,隨著進程申請內存的增加,內存管理器會通過系統調用陷入內核,從而為進程分配更多的內存。

針對堆段的管理,內核提供了兩個系統調用brk和mmap,brk用于更改堆頂地址,而mmap則為進程分配一塊虛擬地址空間。

當進程向glibc申請內存時,如果申請內存的數量大于一個閥值的時候,glibc會采用mmap為進程分配一塊虛擬地址空間,而不是采用brk來擴展堆頂的指針。缺省情況下,此閥值是128K,可以通過函數來修改此值。

#include <malloc.h>

Int mallopt(int param, int value)

Param的取值分別為M_MMAP_THRESHOLD、M_MMAP_MAX。

Value的取值是以字節為單位的。

M_MMAP_THRESHOLD是glibc中申請大塊內存閥值,大于該閥值的內存申請,內存管理器將使用mmap系統調用申請內存,如果小于該閥值的內存申請,內存管理器使用brk系統調用擴展堆頂指針。

M_MMAP_MAX是該進程中最多使用mmap分配地址段的數量。

如果在實際的調試過程中,懷疑某處發生了內存泄露,可以查看該進程的maps表,看進程的堆段或者mmap段的虛擬地址空間是否持續增加,如果是,說明很可能發生了內存泄露,如果mmap段虛擬地址空間持續增加,還可以看到各個段的虛擬地址空間的大小,從而可以確定是申請了多大的內存,對調試內存泄露類問題可以起到很好的定位作用。

? 版權聲明
THE END
喜歡就支持一下吧
點贊11 分享