性能測試的一項重要工作就是檢查有沒有內存泄露。linux下通過top/free/pmap/ps,會提供許多關於內存分配的信息,如top裏面的VIRT,RSS,SWAP,VSZ,RES,SHR等等,到底哪些參數能夠用來檢測memory leak呢?雖然baidu,google很方便,但是一直沒有找到一個令人信服的答案。這些天一直在研究,結合我在以往實際工作中的一些經驗,在此做一個總結:
1,首先使用sar/top/free在系統級確定是否有內存泄露。如有,可以從top輸出確定哪一個process。
2,pmap/top/ps工具是能幫助確定process是否有memory leak。確定memory leak的原則:
A)VIRT/VSZ或者writeable/private (‘pmap –d’輸出)如果在做重複的操作過程中一直保持穩定增長,那麼一定有內存泄露。
B) RSS只能作爲參考,不能用來確定是否有內存泄露。
C) 在performance testing過程中,前面一段時間的內存增長不能用來確定內存泄露。因爲最初系統需要申請一些內存來處理traffic。如果內存在短期就增長數G或者在系統穩定後還在持續增長,那就需要分析了。在我的工作中,一般前面半個小時的內存增長我都忽略。
E)多次申請分配的地址空間可能不連續。在virtual address中有多個[anon]段。 Here 1M was alloated to000000001b56a000 and00002ac25a77c000.
000000001b56a000 1156 12 12 rw--- [ anon ]
00002ac25a77c000 1040 16 16 rw--- [ anon ]
以下的測試可以證明以上的推論。
示例代碼:
#include "stdio.h"
int global_i_init=0;
static int static_global_j=11;
int global_k;
int main () {
int tmp;
printf ("global_i_init: 0x%lx\n",&global_i_init );
printf ("global_k non_init : 0x%lx\n",&global_k );
printf ("static_global_j : 0x%lx\n",&static_global_j );
printf ("stack i : 0x%lx\n",&tmp );
getchar (); // step2
char *x= new char[100*1024];
printf ("data allocate 100k @0x%lx\n", x);
getchar(); // step3
char *x2= new char[1024*1024];
printf ("data allocate 1M i@0x%lx\n", x2);
getchar(); // step4
printf ("data write at x2[1024*1024-1] ");
x2[1024*1024-1]=0;
getchar(); // step5
printf ("data read at x2 ");
int j;
for (int i=0;i<1024*1024; i++)
j=x2[i];
getchar (); // step6
printf ("delete x2");
delete x2;
getchar (); //step 7
printf ("delete x");
delete x;
getchar(); //step8
x2= new char[1024*1024];
printf ("data allocate 1M i@0x%lx\n", x2);
getchar ();
}
程序輸出:
XX48-0-0-1:/root-# ./a.out
global_i_init: 0x500cc0
global_k non_init : 0x500cc4
static_global_j : 0x500cb8
stack i : 0x7fff0120ab5c
data allocate 100k @0x1b56a010
data allocate 1M i@0x2ac25a77f010
data write at x2[1024*1024-1]
data read at x2
delete x2
delete x
data allocate 1M i@0x1b56a010
pmap -d 輸出:
mapped | writeable/private: | shared | ||
STEP 1 | init | 19616K | 236K | 0 |
STEP 2 | new 100k | 19848K | 468K | 0 |
STEP 3 | new 1M | 20876K | 1496K | 0 |
STEP 4 | write 1 byte | 20876K | 1496K | 0 |
STEP 5 | read 1M | 20876K | 1496K | 0 |
STEP 6 | delete 1M | 19848K | 468K | 0 |
STEP 7 | delete 100k | 19848K | 468K | 0 |
STEP 8 | new 1M | 20772K | 1392K | 0 |
pmap -d 結論:
1, mapped 和writeable/private 能夠反映內存的變化.
2, delete 和free 不能在 mapped 和writeable/private 立即反映出來,比方刪除100k就沒有變化.
pmap -x output:
Total kbytes | RSS | Dirty | ||
STEP 1 | init | 19616 | 912 | 124 |
STEP 2 | new 100k | 19848 | 976 | 140 |
STEP 3 | new 1M | 20876 | 980 | 144 |
STEP 4 | write 1 byte | 20876 | 984 | 148 |
STEP 5 | read 1M | 20876 | 2004 | 148 |
STEP 6 | delete 1M | 19848 | 976 | 140 |
STEP 7 | delete 100k | 19848 | 980 | 140 |
STEP 8 | new 1M | 20772 | 984 | 144 |
Address | kbytes | RSS | Dirty | ||
STEP 1 | init | ||||
STEP 2 | new 100k | 000000001b56a000 | 232 | 8 | 8 |
STEP 3 | new 1M | 000000001b56a000 | 232 | 8 | 8 |
STEP 4 | write 1 byte | 000000001b56a000 | 232 | 8 | 8 |
STEP 5 | read 1M | 000000001b56a000 | 232 | 8 | 8 |
STEP 6 | delete 1M | 000000001b56a000 | 232 | 8 | 8 |
STEP 7 | delete 100k | 000000001b56a000 | 232 | 8 | 8 |
STEP 8 | new 1M | 000000001b56a000 | 1156 | 12 | 12 |
Address | kbytes | RSS | Dirty | ||
STEP 1 | init | 00002ac25a77c000 | 12 | 12 | 12 |
STEP 2 | new 100k | 00002ac25a77c000 | 12 | 12 | 12 |
STEP 3 | new 1M | 00002ac25a77c000 | 1040 | 16 | 16 |
STEP 4 | write 1 byte | 00002ac25a77c000 | 1040 | 20 | 20 |
STEP 5 | read 1M | 00002ac25a77c000 | 1040 | 1040 | 20 |
STEP 6 | delete 1M | 00002ac25a77c000 | 12 | 12 | 12 |
STEP 7 | delete 100k | 00002ac25a77c000 | 12 | 12 | 12 |
STEP 8 | new 1M | 00002ac25a77c000 | 12 | 12 | 12 |
pmap -x 結論:
1, 儘管你已經調用了 new分配內存(step 3), 但是 OS不一定一下把所有的page都分配。本例中只有對那一塊已經分配內存進行讀 (STEP5)或者寫(STEP4) 的時候才提交實際內存。
pmap -x:
2, 多次相同的new操作可能在不同的[anon]虛擬地址空間分配。本例中第一個1M分配在00002ac25a77c000, 而第二個1M跑到000000001b56a000。中間跨度很大。
最終pmap -d:
Address Kbytes Mode Offset Device Mapping
0000000000400000 4 r-x-- 0000000000000000 008:00001 a.out
0000000000500000 4 rw--- 0000000000000000 008:00001 a.out
000000001b56a000 1156 rw--- 000000001b56a000 000:00000 [ anon ]
0000003677000000 112 r-x-- 0000000000000000 008:00001 ld-2.5.so
000000367721c000 4 r---- 000000000001c000 008:00001 ld-2.5.so
000000367721d000 4 rw--- 000000000001d000 008:00001 ld-2.5.so
0000003677400000 1336 r-x-- 0000000000000000 008:00001 libc-2.5.so
000000367754e000 2048 ----- 000000000014e000 008:00001 libc-2.5.so
000000367774e000 16 r---- 000000000014e000 008:00001 libc-2.5.so
0000003677752000 4 rw--- 0000000000152000 008:00001 libc-2.5.so
0000003677753000 20 rw--- 0000003677753000 000:00000 [ anon ]
0000003677c00000 520 r-x-- 0000000000000000 008:00001 libm-2.5.so
0000003677c82000 2044 ----- 0000000000082000 008:00001 libm-2.5.so
0000003677e81000 4 r---- 0000000000081000 008:00001 libm-2.5.so
0000003677e82000 4 rw--- 0000000000082000 008:00001 libm-2.5.so
0000003678c00000 52 r-x-- 0000000000000000 008:00001 libgcc_s-4.1.2-20080825.so.1
0000003678c0d000 2048 ----- 000000000000d000 008:00001 libgcc_s-4.1.2-20080825.so.1
0000003678e0d000 4 rw--- 000000000000d000 008:00001 libgcc_s-4.1.2-20080825.so.1
0000003679400000 920 r-x-- 0000000000000000 008:00001 libstdc++.so.6.0.8
00000036794e6000 2044 ----- 00000000000e6000 008:00001 libstdc++.so.6.0.8
00000036796e5000 24 r---- 00000000000e5000 008:00001 libstdc++.so.6.0.8
00000036796eb000 12 rw--- 00000000000eb000 008:00001 libstdc++.so.6.0.8
00000036796ee000 72 rw--- 00000036796ee000 000:00000 [ anon ]
00002ac25a76f000 16 rw--- 00002ac25a76f000 000:00000 [ anon ]
00002ac25a77c000 12 rw--- 00002ac25a77c000 000:00000 [ anon ]
00007fff011f8000 84 rw--- 00007ffffffe9000 000:00000 [ stack ]
00007fff013c2000 12 r-x-- 00007fff013c2000 000:00000 [ anon ]
ffffffffff600000 8192 ----- 0000000000000000 000:00000 [ anon ]
mapped: 20772K writeable/private: 1392K shared: 0K
pmap -x
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 4 4 0 r-x-- a.out
0000000000500000 4 4 4 rw--- a.out
000000001b56a000 1156 12 12 rw--- [ anon ]
0000003677000000 112 96 0 r-x-- ld-2.5.so
000000367721c000 4 4 4 r---- ld-2.5.so
000000367721d000 4 4 4 rw--- ld-2.5.so
0000003677400000 1336 284 0 r-x-- libc-2.5.so
000000367754e000 2048 0 0 ----- libc-2.5.so
000000367774e000 16 16 8 r---- libc-2.5.so
0000003677752000 4 4 4 rw--- libc-2.5.so
0000003677753000 20 16 16 rw--- [ anon ]
0000003677c00000 520 20 0 r-x-- libm-2.5.so
0000003677c82000 2044 0 0 ----- libm-2.5.so
0000003677e81000 4 4 4 r---- libm-2.5.so
0000003677e82000 4 4 4 rw--- libm-2.5.so
0000003678c00000 52 16 0 r-x-- libgcc_s-4.1.2-20080825.so.1
0000003678c0d000 2048 0 0 ----- libgcc_s-4.1.2-20080825.so.1
0000003678e0d000 4 4 4 rw--- libgcc_s-4.1.2-20080825.so.1
0000003679400000 920 404 0 r-x-- libstdc++.so.6.0.8
00000036794e6000 2044 0 0 ----- libstdc++.so.6.0.8
00000036796e5000 24 24 20 r---- libstdc++.so.6.0.8
00000036796eb000 12 12 12 rw--- libstdc++.so.6.0.8
00000036796ee000 72 8 8 rw--- [ anon ]
00002ac25a76f000 16 16 16 rw--- [ anon ]
00002ac25a77c000 12 12 12 rw--- [ anon ]
00007fff011f8000 84 12 12 rw--- [ stack ]
00007fff013c2000 12 4 0 r-x-- [ anon ]
ffffffffff600000 8192 0 0 ----- [ anon ]
---------------- ------ ------ ------
total kB 20772 984 144