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
命令等.如果我让它运行的时间足够长,它将使用大量的内存并淹没整个盒子.
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.
听起来像是泄漏,但它通过了 valgrind
并且只泄漏了几个字节,所有这些都在我期望的地方.调试打印消息表明程序流程符合预期.
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.
通过调试器进一步挖掘,我发现在 main
末尾调用 __run_exit_handlers
时,大部分内存仍在使用中.我可以单步执行对 free
的各种调用,因为它通过全局析构函数链工作.完成这些之后,我只观察到明显内存使用量的最小下降变化.然后,最后它调用_exit()
,然后才将内存一次性恢复到操作系统中.
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 of main
. I can step through various calls to free
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?
推荐答案
这里的一切都基于在 Linux 上运行的 malloc
的 GNU libc 实现.
Everything here is based on GNU libc implementation of malloc
running on Linux.
释放内存后,下面的测试程序似乎没有向系统提供任何内存(strace
没有显示将内存返回给内核的 sbrk
调用):
Test program below does not seem to give away any memory to the system after freeing memory (strace
does not show sbrk
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]);
}
看起来 glibc 根本没有放弃内存.根据 mallopt(3)
手册页,参数 M_TRIM_THRESHOLD
负责释放内存.默认情况下它是 128kb,而测试程序分配和释放 5 GB 的内存.看起来 malloc
实现的其他一些细节不让它释放内存.
Looks like glibc does not give away memory back at all. According to mallopt(3)
man page, parameter M_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 of malloc
implementation do not let it free memory.
目前我可以推荐以下解决方案:
At the moment I can recommend following solutions:
- 如果可以,请尝试调用
malloc_trim
偶尔或在释放大量内存后.这应该强制修剪并应该使用MADV_DONTNEED
将内存返还给操作系统. - 避免使用
malloc
或operator new
分配大量小对象,而是从大小大于M_MMAP_THRESHOLD
的内存池中分配它们.如果程序逻辑允许,之后尝试销毁该池.大小大于M_MMAP_THRESHOLD
的内存块会立即释放回操作系统. - 与前一个相同,但应该更快:使用
mmap
为小对象分配内存池并使用madvise
和MADV_DONTNEED
/MADV_FREE
. - 尝试使用另一个可能利用
MADV_FREE
的分配器将内存返回给系统(jemalloc?).
- 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?).
我在 glibc 的 bugzilla 上找到了这张旧的 (2006) 票证.它说 free
永远不会将内存返回给内核,除非 malloc_trim
被调用.
I have found this old (2006) ticket on glibc's bugzilla. It says there that free
never returns memory back to the kernel, unless malloc_trim
is called.
较新版本的 free
似乎具有执行内部 systrim
函数的代码,该函数应该修剪竞技场的顶部,但我无法使其工作.
Newer versions of free
seem to have code that executes internal systrim
function that should trim top of the arena, but I wasn't able to make it work.
这篇关于glibc 应用程序保留未使用的内存直到退出之前的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!