glibc应用程序会保留未使用的内存,直到退出前 [英] glibc application holding onto unused memory until just before exit
问题描述
我有一个在Linux(Centos 7)上运行的C ++应用程序(gcc 4.9.1,glibc 2.17).它使用各种第三方库,尤其是Boost 1.61.在应用程序运行时,我可以通过htop的VIRT和RES列或ps命令等来观察其内存使用情况稳定增长.如果我让它运行足够长的时间,它将占用大量内存并淹没该盒子.>
听起来像是泄漏,但是它通过valgrind泄漏了仅几个字节,所有这些都在我期望的位置.调试打印消息表明程序流程符合预期.
通过调试器进一步挖掘,发现在main
末尾调用__run_exit_handlers
时,大部分内存仍在使用中.我可以逐步处理对free
的各种调用,因为它在全局析构函数链中起作用.完成这些操作后,我仅观察到表观内存使用量的最小向下变化.然后,最后它调用_exit()
,然后才将内存一次全部恢复到操作系统.
有人可以向我提供有关如何继续调试的其他提示吗?为什么我的程序不退回内存?
此处的所有内容都是基于在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根本不会放弃内存.根据 mallopt(3)
手册页,参数M_TRIM_THRESHOLD
负责释放记忆.默认情况下,它是128kb,而测试程序分配并释放5 GB的内存.看来malloc
实现的其他一些细节不允许它释放内存.
目前,我可以推荐以下解决方案:
- 如果可以,请尝试致电
malloc_trim
偶尔或在释放大量内存之后.这应强制修剪,并应使用MADV_DONTNEED
将内存还给操作系统. - 避免使用
malloc
或operator new
分配大量的小对象,而应从大于M_MMAP_THRESHOLD
的内存池中分配它们.如果程序逻辑允许,请稍后尝试销毁该池.大小大于M_MMAP_THRESHOLD
的内存块会立即释放回OS. - 与上一个相同,但应该更快:使用
mmap
为小对象分配内存池,并使用这张旧票.它说在那里,除非调用malloc_trim
,否则free
绝不会将内存返回给内核.较新版本的
free
似乎具有执行内部systrim
功能的代码,这些功能应该可以缩小舞台的范围,但是我无法使其正常工作.I have a C++ application (gcc 4.9.1, glibc 2.17) running on linux (Centos 7). It uses various third-party libraries, notably Boost 1.61. As the application runs, I can watch its memory usage increasing steadily via htop's VIRT and RES columns, or the ps command, etc. If I let it go long enough, it will use enormous amounts of that memory and swamp the box.
Sounds like a leak, but it passes valgrind with only a few bytes leaked, all in places I'd expect. Debug print messages indicate program flow is as expected.
Digging further via the debugger, I find that most of this memory is still in use when
__run_exit_handlers
is invoked at the end ofmain
. I can step through various calls tofree
as it works through the global destructor chain. After it finishes those, I observe only a minimal downward change in the apparent memory usage. Then, finally it calls_exit()
, and only then is the memory restored to the operating system, all at once.Can anyone offer me additional tips on how to proceed debugging this? Why won't my program give that memory back?
解决方案Everything here is based on GNU libc implementation of
malloc
running on Linux.Test program below does not seem to give away any memory to the system after freeing memory (
strace
does not showsbrk
calls that return memory back to the kernel):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]); }
Looks like glibc does not give away memory back at all. According to
mallopt(3)
man page, parameterM_TRIM_THRESHOLD
is responsible for giving away memory. By default it is 128kb, while test program allocates and frees 5 GB of memory. Looks like some other details ofmalloc
implementation do not let it free memory.At the moment I can recommend following solutions:
- If you can, try calling
malloc_trim
once in a while or after freeing a lot of memory. This should force trimming and should give memory back to OS usingMADV_DONTNEED
. - Avoid allocating large number of small objects using
malloc
oroperator new
, instead allocate them from a memory pool of a size greater thanM_MMAP_THRESHOLD
. Try destroing that pool afterwards if program logic allows this. Memory chunks of size greater thanM_MMAP_THRESHOLD
are immediately released back to OS. - Same as previous one, but should be faster: allocate memory pools for small objects using
mmap
and release memory back to OS usingmadvise
andMADV_DONTNEED
/MADV_FREE
. - Try using another allocator that might take advantage of
MADV_FREE
to return memory back to the system (jemalloc?).
I have found this old (2006) ticket on glibc's bugzilla. It says there that
free
never returns memory back to the kernel, unlessmalloc_trim
is called.Newer versions of
free
seem to have code that executes internalsystrim
function that should trim top of the arena, but I wasn't able to make it work.这篇关于glibc应用程序会保留未使用的内存,直到退出前的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- If you can, try calling