我正在使用gcc 4.3.2在linux(Fedora 10和CentOS 5)中实现我自己的图形库并使用STL容器然后我发现了一些内存问题。当我构建我的图形时,我会使用大量内存来在 top 或其他内存使用工具中查看。我确定我正在释放那个内存(我一次又一次地查看了代码,并使用valgrind来检查内存泄漏),但是内存仍在使用中(我可以在 top 中查看或 cat / proc / meminfo )当我再次创建图形时,它不会增加内存使用量,显然会重用已分配的内存。
经过几天的调试,我创建了一个非常简单的代码,它有同样的问题。
#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;
}
我尝试使用简单数组或我自己的列表类,但在这些情况下,内存通常会被释放。
有谁知道发生了什么事?如何防止这个内存被“保留”?
谢谢!
- 布鲁诺卡波尼
答案 0 :(得分:4)
gcc STL有自己的内存管理层,它可以抓取大块内存并且不会回复它们;你可以设置一个env变量来使它使用原始的新调用
GLIBCPP_FORCE_NEW=1
我认为这也让它自由了。这个env var通常在使用valgrind时使用,这样valgrind就不会认为事情泄露了
答案 1 :(得分:3)
除了STL容器之外,它可能是libc本身这样做的(new / delete - malloc / free实现)。用户空间库可以自由地保留内存以供以后重用。分配/解除分配是一项昂贵的操作(就时钟周期而言),因此许多实现都试图避免这种情况。
答案 2 :(得分:2)
容器类的内存使用由容器的allocator处理(它作为构造函数参数传递给std::list
,默认情况下为std::allocator
。默认分配器的实现可以选择不立即将内存返回给系统,以防止堆的过多碎片。
如果您想要更直接地控制它,您可能必须实现自定义分配器。
答案 3 :(得分:0)
在Linux上,用户内存通过BRK系统调用从内核分配给进程,该进程向下扩展数据指针,使进程可以使用更多内存。这是内核将正常内存传递给进程的唯一方法。也可以通过使用mmap来获取内存,mmap允许进程指定起始地址(除数据指针之外)但没有分配器执行此操作,因为它大大增加了分配器和内核分配器必须的工作量做。出于这个原因,内核可以很容易地回收给用户进程的内存,直到进程终止。如果您的特定应用程序正在生成许多大型alloc / dealloc,那么使用mmap进行这些分配可以是一种解决方案,可以将内存映像大小降低一点。
答案 4 :(得分:0)
我不能确定会发生什么,但问题(如果是问题)是可重复的。
我认为这是一个内存池问题,因为STL在使用的特定分配器中这样做了。 但是,向下钻取列表&lt;&gt;,我发现只是一个“new_allocator”,它不再执行全局新运算符的返回结果:
pointer
allocate(size_type __n, const void* = 0)
{ return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp))); }
据我所知,行为会转到glibc或stdlibc ++内存处理。 在我的快速浏览中,如果自定义分配器肯定会有不同的行为,我无法弄清楚如何在不实现自定义分配器或的情况下实现此行为。
我尝试了没有STL的另一个测试,然后我可以看到资源上下成长。 我建议创建一个自定义分配器,在数组中使用placement new分配任意数量的元素,并关心分配/解除分配这些数组。 Logic告诉我,资源使用必须像“非STL”测试一样。
请尝试并告诉我们会发生什么。我不会自己做,因为我现在没有时间,尽管我很好奇;)
注意:“三巨头”规则对此没有影响。据我所知,没有内存泄漏和对象的内容不相关。布鲁诺可以完成数据复制,自我分配检查等,但只是做了一个空的复制构造函数来说明它的观点。
答案 5 :(得分:0)
我遇到了同样的问题,经过长时间的调试后写了一个示例程序,它说明了它是一些内核(或者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),则在调用free后将释放内存。但是当它使用参数运行时(additionalAlloc为true),只有在程序完成后才释放内存。我在2.6.18-6-xen-amd64内核上使用4.4.5-1 g ++在Debian Squeeze上运行它。我不知道它在其他系统上是如何工作的,但是看到123个字节的额外块会导致不同的程序行为,很有可能它不起作用 - 但请相信我对于这些设置它起作用: - )< / p>
PS。任何人都可以解释为什么33和123值的额外块会导致不同的行为吗?