同事在項目中使用new/delete的時候發現一個奇怪的現象:
int32_t i;
std::queue<char *> qTest;
for (i = 0; i < 100000; i ++) {
char *p = new char[100];
qTest.push(p);
char *p1 = qTest.front();
delete[] p1;
qTest.pop();
}
當在一個迴圈內,如果申請空間,在迴圈內並釋放掉,記憶體不會引起增長,即使重複上面的單元也不會增長記憶體,但是當:
int32_t i;
std::queue<char *> qTest;
for (i = 0; i < 100000; i ++) {
char *p = new char[100];
qTest.push(p);
//char *p1 = qTest.front();
//delete[] p1;
//qTest.pop();
}
while( !qTest.empty() ) {
char *p1 = qTest.front();
delete[] p1;
qTest.pop();
}
而發現通過delete[]後,記憶體並沒有減少,即使重複上面的單元,記憶體只會增加,不會減少,用valgrind的memcheck工具查看,也無記憶體流失。
其實,這個與malloc的實現有關,一般來說,系統都會有預設的malloc行為,一般來說(針對FreeBSD和Linux系列),對於大於1M的資料,malloc行為會直接調用系統的介面,直接向作業系統申請一塊比資料區塊更大記憶體,然後劃分成若干過chunk單元(這裡會有一些演算法設計),而這些chunk分配給資料後,肯定還會剩下很多的trunk單元,留給以後的分配用,但這裡耗費系統資源,代價較大;而對於小於等於1M的資料,則malloc行為會利用那些trunk單元,佔用系統資源少,速度快。同理free的時候,資料佔用的記憶體被釋放,如果大於1M,則系統會回收掉記憶體,節省資源,而小於等於1M的資料,則記憶體釋放時,不會還給作業系統,以空trunk形式存在,並由當前進程空間維護著。說到底,這種策略就是一個記憶體池的概念。
記憶體池靈活度更加方便,一般來說(針對FreeBSD和Linux系列),每個trunk最小容納的位元組數是16bytes,而自行設計的記憶體池,可以更加最佳化,節省大量產生的記憶體片段,像上面的代碼就是因為記憶體雖然釋放,但是這些記憶體都沒有還給作業系統,導致記憶體片段越來越多,最好的設計方式就是使用一個記憶體池,針對經常使用的分配大小,可以多分配這樣的trunk,而其他大小的trunk可以少產生,從而達到最佳化目的。
另外,也可以使用mallopt來直接調整malloc的行為:
int mallopt (int PARAM, int VALUE)
When calling `mallopt', the PARAM argument specifies the parameter
to be set, and VALUE the new value to be set. Possible choices
for PARAM, as defined in `malloc.h', are:
`M_TRIM_THRESHOLD'
This is the minimum size (in bytes) of the top-most,
releasable chunk that will cause `sbrk' to be called with a
negative argument in order to return memory to the system.
`M_TOP_PAD'
This parameter determines the amount of extra memory to
obtain from the system when a call to `sbrk' is required. It
also specifies the number of bytes to retain when shrinking
the heap by calling `sbrk' with a negative argument. This
provides the necessary hysteresis in heap size such that
excessive amounts of system calls can be avoided.
`M_MMAP_THRESHOLD'
All chunks larger than this value are allocated outside the
normal heap, using the `mmap' system call. This way it is
guaranteed that the memory for these chunks can be returned
to the system on `free'. Note that requests smaller than
this threshold might still be allocated via `mmap'.
`M_MMAP_MAX'
The maximum number of chunks to allocate with `mmap'.
Setting this to zero disables all use of `mmap'.
值得注意的是mallopt是malloc底層的函數,需要使用info mallopt來查看相關協助資訊。
[From:http://my.huhoo.net/archives/2010/05/malloptmallocnew.html]
關於mallopt:
http://www.man7.org/linux/man-pages/man3/mallopt.3.html