c++ - glibc 应用程序保留未使用的内存直到退出

标签 c++ linux memory-leaks glibc

我有一个在 Linux (Centos 7) 上运行的 C++ 应用程序(gcc 4.9.1、glibc 2.17)。它使用各种第三方库,特别是 Boost 1.61。在应用程序运行时,我可以通过 htopVIRTRES 列或 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 将内存返回给操作系统。
  • 避免使用 mallocoperator new 分配大量小对象,而是从大小大于 M_MMAP_THRESHOLD 的内存池中分配它们.如果程序逻辑允许,之后尝试销毁该池。大小大于 M_MMAP_THRESHOLD 的内存块会立即释放回操作系统。
  • 与上一个相同,但应该更快:使用 mmap 为小对象分配内存池,并使用 madvise 将内存释放回操作系统。和 MADV_DONTNEED/MADV_FREE
  • 尝试使用另一个可能利用 MADV_FREE 的分配器将内存返回给系统(jemalloc?)。

我找到了 this glibc 的 bugzilla 上的旧 (2006) 票。它在那里说 free 永远不会将内存返回给内核,除非调用 malloc_trim

较新版本的 free 似乎具有执行内部 systrim 函数的代码,该函数应该修剪 arena 的顶部,但我无法使其工作。

关于c++ - glibc 应用程序保留未使用的内存直到退出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48651432/

相关文章:

java - 使用 Java JNI 时是否可以调试核心转储?

正则表达式 sed 问题

linux - Bash 脚本在 IF 之后不按文件类型排序

c - 内存泄漏和无效读/写

c++ - 默认模板参数是否符合单一定义规则?

c++ - 视觉C++ : buggy towupper

c++ - QT是否支持运行时动态UI生成和HTML渲染

linux - 为什么我的守护程序在我注销时没有终止?

objective-c - UIImagePickerController和UIPopoverController两个内存泄漏

c - 动态分配内存