昨天使用STL(仅指gcc的SGI STL,VC,borlandC的不同)时,用valgrind测试,总是发现有"not-freed blocks", 于是我满脸迷惑的得出结论:使用SGI STL会造成内存泄漏.
现在才知道使用了多年C/C++的我竟然并不清楚什么叫"内存泄漏".下面是我的一些心得,欢迎大家批评指正.
1. "只要分配了内存没有释放,就会导致内存泄漏" -- 这是我以前的理解, 是片面的.
分配了的内存,如果它的指针没有丢失,就不算是泄漏. 一般说来,为static指针变量或全局的指针变量(它们的生存期是全局的)进行内存分配,如果没有释放它,虽然这也是"not-freed blocks",但是它是"reachable"的.现代的OS会得到这些指针并去释放它.
下面是两个例子:
1) 使用全局指针或静态指针:
int main()
{
static int *pi; // use static pointer: not a real memory leak!!!
pi = new int;
return 0;
}
使用valgrind检查:
valgrind --leak-check=yes --show-reachable=yes -q ./memtest
==13764== searching for pointers to 1 not-freed blocks.
==13764== checked 4488448 bytes.
==13764==
==13764== 4 bytes in 1 blocks are still reachable in loss record 1 of 1
==13764== at 0xB74D27AB: __builtin_new (vg_replace_malloc.c:172)
==13764== by 0xB74D2802: operator new(unsigned) (vg_replace_malloc.c:185)
==13764== by 0x8048491: main (in /home/prog/valgrind/memtest)
==13764== by 0xB71C4BA6: __libc_start_main (in /lib/libc-2.3.2.so)
==13764==
==13764== LEAK SUMMARY:
==13764== definitely lost: 0 bytes in 0 blocks.
==13764== possibly lost: 0 bytes in 0 blocks.
==13764== still reachable: 4 bytes in 1 blocks.
==13764== suppressed: 0 bytes in 0 blocks.
==13764==
报告说有一个"not-freed block",但这个block是reachable的,所以它不算是真正的memory leak.
2) 如果使用的是局部指针:
int main()
{
int *pi; // use local pointer: real memory leak!!!
pi = new int[100];
return 0;
}
==13782== searching for pointers to 1 not-freed blocks.
==13782== checked 4484352 bytes.
==13782==
==13782== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==13782== at 0xB74D27AB: __builtin_new (vg_replace_malloc.c:172)
==13782== by 0xB74D2802: operator new(unsigned) (vg_replace_malloc.c:185)
==13782== by 0x8048491: main (in /home/prog/valgrind/memtest)
==13782== by 0xB71C4BA6: __libc_start_main (in /lib/libc-2.3.2.so)
==13782==
==13782== LEAK SUMMARY:
==13782== definitely lost: 4 bytes in 1 blocks.
==13782== possibly lost: 0 bytes in 0 blocks.
==13782== still reachable: 0 bytes in 0 blocks.
==13782== suppressed: 0 bytes in 0 blocks.
==13782==
报告说是"definitely lost",这才是真正的memory leak!
2. 当然,我们一般情况下是不会分配内存而不释放的. 它的典型应用便是著名的memory pool (我先前居然并不了解,寒~ 幸好现在知道了). 其要领是为了提高内存管理的效率,针对特殊需要(如要分配的内存是等大的并且很小)去重载new,delete操作符. 它会避免每次申请很小的内存,而是一下子得到很大的一块,再自己去定义和使用.(出于效率的考虑,它最后并不释放)
我花了一下午时间分析的我用的SGI STL的内存分配源码(我是在LINUX下使用g++的),
下面一段是在/usr/include/c++/3.2.3/bits/stl_alloc.h中找到的STL源码:
template<bool __threads, int __inst>
class __default_alloc_template
{
private:
enum {_ALIGN = 8};
enum {_MAX_BYTES = 128};
enum {_NFREELISTS = _MAX_BYTES / _ALIGN};
union _Obj
{
union _Obj* _M_free_list_link;
char _M_client_data[1]; // The client sees this.
};
static _Obj* volatile _S_free_list[_NFREELISTS];
// Chunk allocation state.
static char* _S_start_free;
static char* _S_end_free;
static size_t _S_heap_size;
static _STL_mutex_lock _S_node_allocator_lock;
....
可以看到,在关键的数据结构上,它使用了静态的指针,并且定义了一些常量来控制内存块的大小等.
3. 对于这种技术是不是"隐式的内存泄漏" , 我认为不是.
隐式的内存泄漏指分配内存后,虽然也释放,但没有及时释放,导致内存消耗无法及时回收.在服务器编程时特别忌讳.
一般说来, 内存池的大小是可控, 不会一直增长下去. 从应用的层面上来说,它也是"释放"了内存了的.
现在才知道使用了多年C/C++的我竟然并不清楚什么叫"内存泄漏".下面是我的一些心得,欢迎大家批评指正.
1. "只要分配了内存没有释放,就会导致内存泄漏" -- 这是我以前的理解, 是片面的.
分配了的内存,如果它的指针没有丢失,就不算是泄漏. 一般说来,为static指针变量或全局的指针变量(它们的生存期是全局的)进行内存分配,如果没有释放它,虽然这也是"not-freed blocks",但是它是"reachable"的.现代的OS会得到这些指针并去释放它.
下面是两个例子:
1) 使用全局指针或静态指针:
int main()
{
static int *pi; // use static pointer: not a real memory leak!!!
pi = new int;
return 0;
}
使用valgrind检查:
valgrind --leak-check=yes --show-reachable=yes -q ./memtest
==13764== searching for pointers to 1 not-freed blocks.
==13764== checked 4488448 bytes.
==13764==
==13764== 4 bytes in 1 blocks are still reachable in loss record 1 of 1
==13764== at 0xB74D27AB: __builtin_new (vg_replace_malloc.c:172)
==13764== by 0xB74D2802: operator new(unsigned) (vg_replace_malloc.c:185)
==13764== by 0x8048491: main (in /home/prog/valgrind/memtest)
==13764== by 0xB71C4BA6: __libc_start_main (in /lib/libc-2.3.2.so)
==13764==
==13764== LEAK SUMMARY:
==13764== definitely lost: 0 bytes in 0 blocks.
==13764== possibly lost: 0 bytes in 0 blocks.
==13764== still reachable: 4 bytes in 1 blocks.
==13764== suppressed: 0 bytes in 0 blocks.
==13764==
报告说有一个"not-freed block",但这个block是reachable的,所以它不算是真正的memory leak.
2) 如果使用的是局部指针:
int main()
{
int *pi; // use local pointer: real memory leak!!!
pi = new int[100];
return 0;
}
==13782== searching for pointers to 1 not-freed blocks.
==13782== checked 4484352 bytes.
==13782==
==13782== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==13782== at 0xB74D27AB: __builtin_new (vg_replace_malloc.c:172)
==13782== by 0xB74D2802: operator new(unsigned) (vg_replace_malloc.c:185)
==13782== by 0x8048491: main (in /home/prog/valgrind/memtest)
==13782== by 0xB71C4BA6: __libc_start_main (in /lib/libc-2.3.2.so)
==13782==
==13782== LEAK SUMMARY:
==13782== definitely lost: 4 bytes in 1 blocks.
==13782== possibly lost: 0 bytes in 0 blocks.
==13782== still reachable: 0 bytes in 0 blocks.
==13782== suppressed: 0 bytes in 0 blocks.
==13782==
报告说是"definitely lost",这才是真正的memory leak!
2. 当然,我们一般情况下是不会分配内存而不释放的. 它的典型应用便是著名的memory pool (我先前居然并不了解,寒~ 幸好现在知道了). 其要领是为了提高内存管理的效率,针对特殊需要(如要分配的内存是等大的并且很小)去重载new,delete操作符. 它会避免每次申请很小的内存,而是一下子得到很大的一块,再自己去定义和使用.(出于效率的考虑,它最后并不释放)
我花了一下午时间分析的我用的SGI STL的内存分配源码(我是在LINUX下使用g++的),
下面一段是在/usr/include/c++/3.2.3/bits/stl_alloc.h中找到的STL源码:
template<bool __threads, int __inst>
class __default_alloc_template
{
private:
enum {_ALIGN = 8};
enum {_MAX_BYTES = 128};
enum {_NFREELISTS = _MAX_BYTES / _ALIGN};
union _Obj
{
union _Obj* _M_free_list_link;
char _M_client_data[1]; // The client sees this.
};
static _Obj* volatile _S_free_list[_NFREELISTS];
// Chunk allocation state.
static char* _S_start_free;
static char* _S_end_free;
static size_t _S_heap_size;
static _STL_mutex_lock _S_node_allocator_lock;
....
可以看到,在关键的数据结构上,它使用了静态的指针,并且定义了一些常量来控制内存块的大小等.
3. 对于这种技术是不是"隐式的内存泄漏" , 我认为不是.
隐式的内存泄漏指分配内存后,虽然也释放,但没有及时释放,导致内存消耗无法及时回收.在服务器编程时特别忌讳.
一般说来, 内存池的大小是可控, 不会一直增长下去. 从应用的层面上来说,它也是"释放"了内存了的.