Joh*_*n S 6 c++ linux memory-leaks glibc
我有一个在 Linux(Centos 7)上运行的 C++ 应用程序(gcc 4.9.1,glibc 2.17)。它使用各种第三方库,尤其是 Boost 1.61。当应用程序运行时,我可以通过htop
'sVIRT
和RES
列,或ps
命令等观察它的内存使用量稳步增加。如果我让它运行足够长的时间,它将使用大量的内存并淹没盒子。
听起来像是泄漏,但它通过valgrind
时只泄漏了几个字节,所有这些都在我期望的地方。调试打印消息表明程序流程符合预期。
通过调试器进一步挖掘,我发现__run_exit_handlers
在main
. 我可以逐步执行各种调用,free
因为它通过全局析构函数链工作。完成这些之后,我只观察到明显内存使用量的最小向下变化。然后,最后它调用_exit()
,然后才立即将内存恢复到操作系统。
任何人都可以向我提供有关如何进行调试的其他提示吗?为什么我的程序不还给那个内存?
这里的一切都基于malloc
在 Linux上运行的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]);
}
Run Code Online (Sandbox Code Playgroud)
看起来 glibc 根本没有放弃内存。根据mallopt(3)
手册页,参数M_TRIM_THRESHOLD
负责放弃内存。默认情况下它是 128kb,而测试程序分配和释放 5 GB 的内存。看起来其他一些malloc
实现细节不允许它释放内存。
目前我可以推荐以下解决方案:
malloc_trim
偶尔或在释放大量内存后调用。这应该强制修剪并且应该使用MADV_DONTNEED
.malloc
或分配大量小对象operator new
,而是从大小大于 的内存池中分配它们M_MMAP_THRESHOLD
。如果程序逻辑允许,之后尝试销毁该池。大小大于 的内存块M_MMAP_THRESHOLD
会立即释放回操作系统。mmap
使用madvise
和MADV_DONTNEED
/为小对象分配内存池并释放内存回操作系统MADV_FREE
。MADV_FREE
将内存返回给系统的分配器(jemalloc?)。我在 glibc 的 bugzilla 上找到了这张旧的(2006 年)票。它说那里free
永远不会将内存返回给内核,除非malloc_trim
被调用。
较新版本的free
似乎有执行内部systrim
函数的代码,应该修剪竞技场的顶部,但我无法使其工作。