天天看點

Linux核心之記憶體3: 程序的記憶體消耗和記憶體洩漏

1.程序的VMA

(1)程序位址空間

在Linux系統中,每個程序都有自己的虛拟記憶體空間0~3G;

核心空間隻有一個3G~4G;

Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏

程序通過系統API調用,在核心空間申請記憶體,不統計在任何使用者程序;程序消耗記憶體,單指使用者空間記憶體消耗;

(2)VMA清單

LINUX用task_struct來描述程序,其中的mm_struct是描述記憶體的結構體,mm_struct有一個vma清單,管理目前程序的所有vma段。

Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏

每個程序的記憶體由多個vma段組成:

(3)檢視VMA方法:

1.pmap

Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏

由圖知,從接近0位址開始,第一個4K是隻讀代碼段,第二個4K是隻讀資料段,還有其他共享庫代碼段,堆棧等;

可見一個程序的VMA涵蓋多個位址區域**,但并沒有覆寫所有位址空間**。VMA未覆寫的位址空間是illegal的,通路這些位址,缺頁中斷,發生pagefault.

2.cat /proc/pid/maps

Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏

讀檔案形式,與pmap一一對應;

3.cat /proc/pid/smaps

更詳細的描述

00400000-00401000 r-xp 00000000 08:15 20316320 /home/leon/work/linux/mm/a.out
Size:         4 kb
KernelPageSize:    4 kB
MMUPageSize:      4 kB
Rss:          4 kB
Pss:          4 kB
Shared_Clean:     0 kB
Shared_Dirty:     0 kB
Private_Clean:     4 kB
Private_Dirty:     0 kB
Referenced:      4 kB
Anonymous:       0 kB
LazyFree:       0 kB
AnonHugePages:     0 kB
ShmemPmdMapped:    0 kB
Shared_Hugetlb:    0 kB
Private_Hugetlb:    0 kB
Swap:         0 kB
SwapPss:        0 kB
Locked:        0 kB
VmFlags: rd ex mr mw me dw sd      

檢視VMA的三個方法對比

Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏

VMA的來源,代碼段,資料段,堆棧段。

Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏

VMA是linux最核心資料結構之一。

2.page fault的幾種可能性,major和minor

Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏

mmu給cpu發page fault時,可以從寄存器讀到兩個元素,pagefault位址,pagefault原因。

(1)通路Heap堆(首次申請,不是從Libc擷取),第一次寫,發生pagefault,linux檢查VMA權限,發現權限合法,發缺頁中斷,申請一頁記憶體,并更新頁表。

(2)通路空區域,通路非法,發段錯誤;

(3)通路代碼段, 在此區域寫,報pagefault,檢查權限發現錯誤,報段錯誤;

(4)通路代碼段,在此區域讀/執行,linux檢查權限合法,若代碼不在記憶體,那麼申請記憶體頁,把代碼從硬碟讀到記憶體。

伴随I/O的pagefault, 叫major pagefault, 否則minor pagefault.

major pagefault耗時遠大于 minor pagefault.

\time -v python hello.py      

3.記憶體是如何被瓜分的: :vss、rss、pss和uss

rss是不是代表程序的記憶體消耗呢,NO。

Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏

VSS:單個程序全部可通路的位址空間,但未必所有虛拟位址都已經映射實體記憶體;

RSS:駐留記憶體,單個程序實際占用的實體記憶體大小(不十分準确的描述);上圖的程序1

PSS:

比例化的記憶體消耗,相對RSS,将共享記憶體空間按被共享程序數量比例化;上圖的C庫4被三個程序共享,是以4/3;

USS:程序獨占記憶體,比如上圖的堆6。

案例,連續運作兩次a.out,檢視記憶體占用情況

運作程式a.out

./a.out &
pidof a.out
cat /proc/pid/smaps |more      
Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏
&      
Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏
cat /proc/pid_2/smaps |more      

PSS變化

shared_clean

private_clean

4.應用記憶體洩漏的界定方法

Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏

統計系統的記憶體消耗,檢視PSS。

檢查有沒有記憶體洩漏,檢查USS

./smem

smem –pie=command      
Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏
smem –bar=command      
Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏
Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏

android裡面有類似的工具,procmem/procrank

smem分析系統記憶體使用是通過/proc/smaps的,procrank是通過分析/proc/kpagemap。

5.應用記憶體洩漏的檢測方法:valgrind和addresssanitizer

其他查詢記憶體洩漏工具:

valgrind:

  gcc -g leak-example.c -o leak-example

  valgrind --tool=memcheck --leak-check=yes ./leak-example      
Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏

20468 HEAP SUMMARY:

20468 in use at exit: 270,336 bytes in 22 blocks

20468 total heap usage: 44 allocs, 22 frees, 292,864 bytes allocated

20468

20468 258,048 bytes in 21 blocks are definitely lost in loss record 2 of 2

20468 at 0x4C2DB8F: malloc (in

/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)

20468 by 0x4005C7: main (leak-example.c:6)

Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏

valgrid是在虛拟機跑APP,速度很慢;

新版gcc4.9以後內建了asan

asan:

  gcc -g -fsanitize=address ./leak-example.c

  gcc -fuse-ld=gold -fsanitize=address -g ./lsan.c      
Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏

#1 0x40084e in main lsan.c:9

記憶體洩漏不一定在使用者空間,排查核心空間,檢測slab , vmalloc

slaptop, 檢查申請和釋放不成對。

在核心編譯,打開kmemleak選項。

5.工程調試記憶體洩漏問題一般步驟:

(1) meminfo, free 多點采樣确認有記憶體洩漏。

(2)定位,smem檢查使用者空間

Linux核心之記憶體3: 程式的記憶體消耗和記憶體洩漏

USS在不斷增加。

cat      
cat /proc/meminfo

cat /proc/buddyinfo

cat /proc/zoneinfo

cat      

繼續閱讀