我有一个在Linux(Centos 7)上运行的C ++应用程序(gcc 4.9.1,glibc 2.17)。它使用各种第三方库,特别是Boost 1.61。当应用程序运行时,我可以通过htop的VIRT和RES列或者ps命令等来观察其内存使用量的稳定增长。如果我让它足够长,它将使用大量的内存并淹没盒子。
听起来像是一个漏洞,但它传递的valgrind只有几个字节泄漏,所有这些都在我期望的地方。调试打印消息表明程序流程符合预期。
通过调试器进一步挖掘,我发现在__run_exit_handlers
结束时调用main
时,大部分内存仍在使用中。我可以逐步调用free
,因为它通过全局析构函数链工作。完成后,我观察到表观内存使用量只有极小的向下变化。然后,最后它调用_exit()
,然后才将内存恢复到操作系统,一次性完成。
有人可以向我提供有关如何进行调试的其他提示吗?为什么我的程序不能回忆这段记忆?
答案 0 :(得分:2)
此处的所有内容都基于在Linux上运行的malloc
的GNU libc实现。
下面的测试程序在释放内存后似乎没有给系统留下任何内存(strace
没有显示将内存返回内核的sbrk
调用):
int main()
{
static const int N = 5000000;
static void *arr[N];
for (int i = 0; i < N; i++)
arr[i] = std::malloc(1024);
// reverse to simplify allocators job
for (int i = N - 1; i >= 0; i--)
std::free(arr[i]);
}
看起来glibc根本没有放弃记忆。根据{{3}}手册页,参数M_TRIM_THRESHOLD
负责释放内存。默认情况下它是128kb,而测试程序分配并释放5 GB内存。看起来malloc
实现的其他一些细节不会让它释放内存。
目前我可以推荐以下解决方案:
mallopt(3)
。这应该强制修剪,并且应该使用MADV_DONTNEED
将内存返回给操作系统。malloc
或operator new
分配大量小对象,而是从大小超过M_MMAP_THRESHOLD
的内存池中分配它们。如果程序逻辑允许,请尝试在之后销毁该池。大小大于M_MMAP_THRESHOLD
的内存块会立即释放回操作系统。mmap
为小对象分配内存池,并使用malloc_trim
和MADV_DONTNEED
/ MADV_FREE
将内存释放回操作系统。 MADV_FREE
的分配器将内存返回给系统(jemalloc?)。我在glibc的bugzilla上找到了madvise
旧(2006)票。它说free
永远不会将内存返回给内核,除非调用malloc_trim
。
free
的较新版本似乎有执行内部systrim
函数的代码,该代码应修剪竞技场的顶部,但我无法使其正常工作。
答案 1 :(得分:0)
您可以使用valgrind --tool=massif ./executable
查看http://valgrind.org/docs/manual/ms-manual.html
上的文档然后,一旦分析了数据,就可以应用内存池和其他技术。既然你已经使用了Boost,你可以在Boost中找到几个这样的工具。