STL容器内存问题

我正在用gcc 4.3.2在linux(Fedora 10和CentOS 5)中实现我自己的图库,并使用STL容器,然后我发现内存有问题。 当我build立我的图表时,我使用了足够多的内存来查看顶部或另一个内存使用工具。 我确定我正在释放内存(我一遍又一遍地检查了代码,并使用了valgrind来检查内存泄漏),但内存仍然在使用中(我可以在顶部cat / proc / meminfo中查看)而当我再次创buildgraphics时,它不会增加内存使用量,显然重新使用分配的内存。

经过几天的debugging,我创build了一个非常简单的代码,有同样的问题。

#include <iostream> #include <list> // Object that occupies 128KB. // Data is not important. class MyObject { public: int * a; int * b; int * c; int * d; MyObject( ) { a = new int[ 8192 ]; b = new int[ 8192 ]; c = new int[ 8192 ]; d = new int[ 8192 ]; } MyObject( const MyObject & m ) { a = new int[ 8192 ]; b = new int[ 8192 ]; c = new int[ 8192 ]; d = new int[ 8192 ]; } ~MyObject( ) { delete [] a; delete [] b; delete [] c; delete [] d; } void operator=( const MyObject &m ) { //Do nothing. } }; typedef std::list< MyObject > list_t; #define MB_TO_ALLOC 1000 // Size in MB that the program must alloc. #define SLEEP_TIME 5 // Time in seconds that the program must wait until go to another step. // It's used to give sufficient time for tools update the memory usage int main( ) { std::cout << "Alloc..." << std::endl; list_t * list = new list_t( ); // Number of objects for alloc MB_TO_ALLOC amount of memory int nObjects = MB_TO_ALLOC * 1024 / 128; for( int i = 0; i < nObjects; ++i ) list->push_back( MyObject( ) ); std::cout << SLEEP_TIME << "s to Dealloc..." << std::endl; // Wait some time for a tool (like top) to update the memory usage sleep( SLEEP_TIME ); std::cout << "Dealloc..." << std::endl; delete list; std::cout << SLEEP_TIME << "s to Alloc..." << std::endl; // Wait some time for a tool (like top) to update the memory usage sleep( SLEEP_TIME ); //Repeats the procedure for evaluating the reuse of memory std::cout << "Alloc..." << std::endl; list = new list_t( ); for( int i = 0; i < nObjects; ++i ) list->push_back( MyObject( ) ); std::cout << SLEEP_TIME << "s to Dealloc..." << std::endl; sleep( SLEEP_TIME ); delete list; } 

我试图使用简单的数组或我自己的列表类,但在这些情况下,内存正常释放。

有谁知道发生了什么事? 如何防止这个内存被“保留”?

谢谢!

– 布鲁诺Caponi

Solutions Collecting From Web of "STL容器内存问题"

海湾合作委员会STL有自己的内存管理层,抓住大块的内存,不给他们回来; 有一个env变量可以设置使它使用原始的新的调用

 GLIBCPP_FORCE_NEW=1 

我认为这也使它免费。 这个env var通常在使用valgrind时使用,以便valgrind不会认为泄漏的东西

除了STL容器,它可能是libc自己做的(new / delete-malloc / free实现)。 用户空间库可以保留内存以供以后重用。 分配/解除分配是一个昂贵的操作(在时钟周期方面),所以很多实现尝试避免这种情况。

内存使用的容器类是由容器的分配器 (它作为构造函数参数传递给std::list ,默认情况下是std::allocator )。 默认分配器的实现可以选择不立即将内存返回给系统,以防止堆的过度分段。

如果你想更直接的控制这个,你可能需要实现一个自定义的分配器。

在Linux上,用户内存被BRK系统调用从内核分配给一个进程,这个进程向下扩展了数据指针,使进程有更多的内存。 这是内核将正常内存传递给进程的唯一方式。 也可以通过使用mmap来获得内存,这允许进程指定一个起始地址(除了数据指针),但没有分配器这样做,因为它极大地增加了分配器和内核分配器必须的工作量做。 由于这个原因,内核给予用户进程很容易被内核回收,直到进程终止。 如果您的特定应用程序正在进行大量的alloc / deallocs,那么使用mmap作为这些分配可以是一个解决方案,以保持内存图像的大小。

我不能确定会发生什么,但问题(如果是问题)是可重现的。

我以为这是一个内存池的问题,徘徊STL这样做在具体的分配器使用。 然而,钻取列表<>时,我发现只是一个“new_allocator”,它不会返回全局new运算符的返回结果:

 pointer allocate(size_type __n, const void* = 0) { return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp))); } 

我的理解是,行为转到glibc或stdlibc ++内存处理。 在我的快速查看中,我无法弄清楚如何在不实现自定义分配器的情况下防止这种行为, 也不知道如果自定义分配器肯定会有不同的行为。

我尝试了另一个没有STL的测试,然后我可以看到资源成长和下降。 我建议创建一个自定义的分配器,分配一个数组中的任意数量的元素与新的位置和关心分配/取消分配这些数组。 逻辑告诉我,资源使用必须像“非STL”测试一样。

请尝试一下,告诉我们发生了什么事。 我不会自己做,因为我现在没有时间空闲,尽管我好奇;)

注:“三巨头”的规则在这里没有影响。 据我所知,没有内存泄漏对象的内容是不相关的。 布鲁诺可以完成数据复制,自我分配检查等,但只是做了一个空的复制构造函数来说明它的观点。

我遇到了同样的问题,并经过长时间的调试写了示例程序,这ilustrates,这是一些内核(或可能是g ++)的问题。

 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <ctime> static const size_t CHUNKS_COUNT = 1024 * 1024; void* chunks[CHUNKS_COUNT]; int main(int argc, const char *argv[]) { bool additionalAlloc = false; if (argc > 1) additionalAlloc = true; fprintf(stdout, "%lu: starting allocating chunks, additionalAlloc=%d\n", time(NULL), additionalAlloc); for (size_t n = 0; n < 2; ++n) { void* additionalChunk; // 1GB for (size_t c = 0; c < CHUNKS_COUNT; ++c) { static const size_t BASE_CHUNK_SIZE = 1024; chunks[c] = malloc(BASE_CHUNK_SIZE); } if (additionalAlloc) { // 33 is arbitrary, but for instance for 123 given example // is not working - magic :-) additionalChunk = malloc(33); } fprintf(stdout, "%lu: finished allocating chunks, n=%lu\n", time(NULL), n); sleep(60); for (size_t c = 0; c < CHUNKS_COUNT; ++c) { free(chunks[c]); } if (additionalAlloc) free(additionalChunk); fprintf(stdout, "%lu: finished freeing chunks, n=%lu\n", time(NULL), n); sleep(60); } sleep(60); fprintf(stdout, "%lu: finishing program\n", time(NULL)); return 0; } 

当它没有参数运行(additionalAlloc为false)时,释放内存后释放到系统。 但是当它使用参数运行(additionalAlloc为true)时,只有在程序结束后才能释放内存。 我在2.6.18-6-xen-amd64内核上使用4.4.5-1 g ++在Debian Squeeze上运行它。 我不知道它是如何在其他系统上工作的,但是看到123个字节的额外块会导致不同的程序行为,很有可能它不起作用 – 但是请相信我,对于这些设置它工作:-)

PS。 任何人都可以解释为什么33和123值更多的块导致不同的行为?