【问题标题】:Understanding glibc malloc trimming了解 glibc malloc 修整
【发布时间】:2016-12-03 07:55:56
【问题描述】:

我目前正在处理的某些程序消耗的内存比我认为的要多得多。所以我试图了解 glibc malloc 修剪的工作原理。我写了以下测试:

#include <malloc.h>
#include <unistd.h>

#define NUM_CHUNKS 1000000
#define CHUNCK_SIZE 100

int main()
{
    // disable fast bins
    mallopt(M_MXFAST, 0);

    void** array  = (void**)malloc(sizeof(void*) * NUM_CHUNKS);

    // allocating memory
    for(unsigned int i = 0; i < NUM_CHUNKS; i++)
    {
        array[i] = malloc(CHUNCK_SIZE);
    }

    // releasing memory ALMOST all memory
    for(unsigned int i = 0; i < NUM_CHUNKS - 1 ; i++)
    {
        free(array[i]);
    }

    // when enabled memory consumption reduces
    //int ret = malloc_trim(0);
    //printf("ret=%d\n", ret);

    malloc_stats();

    sleep(100000);
}

测试输出(不调用 malloc_trim):

Arena 0:
system bytes     =  112054272
in use bytes     =        112
Total (incl. mmap):
system bytes     =  120057856
in use bytes     =    8003696
max mmap regions =          1
max mmap bytes   =    8003584

尽管几乎所有内存都被释放,但此测试代码消耗的常驻内存比预期的要多:

[root@node0-b3]# ps aux | grep test
root     14662  1.8  0.4 129736 **118024** pts/10  S    20:19   0:00 ./test

过程图:

0245e000-08f3b000 rw-p 00000000 00:00 0                                  [heap]
Size:             109428 kB
Rss:              109376 kB
Pss:              109376 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:    109376 kB
Referenced:       109376 kB
Anonymous:        109376 kB
AnonHugePages:         0 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB
VmFlags: rd wr mr mw me ac 
7f1c60720000-7f1c60ec2000 rw-p 00000000 00:00 0 
Size:               7816 kB
Rss:                7816 kB
Pss:                7816 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:      7816 kB
Referenced:         7816 kB
Anonymous:          7816 kB
AnonHugePages:         0 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB

当我启用对 malloc_trim 的调用时,测试的输出几乎保持不变:

ret=1
Arena 0:
system bytes     =  112001024
in use bytes     =        112
Total (incl. mmap):
system bytes     =  120004608
in use bytes     =    8003696
max mmap regions =          1
max mmap bytes   =    8003584

但是,RSS 显着下降:

[root@node0-b3]# ps aux | grep test
root     15733  0.6  0.0 129688  **8804** pts/10   S    20:20   0:00 ./test

进程映射(在 malloc_trim 之后):

01698000-08168000 rw-p 00000000 00:00 0                                  [heap]
Size:             109376 kB
Rss:                   8 kB
Pss:                   8 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         8 kB
Referenced:            8 kB
Anonymous:             8 kB
AnonHugePages:         0 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB
VmFlags: rd wr mr mw me ac 
7f508122a000-7f50819cc000 rw-p 00000000 00:00 0 
Size:               7816 kB
Rss:                7816 kB
Pss:                7816 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:      7816 kB
Referenced:         7816 kB
Anonymous:          7816 kB
AnonHugePages:         0 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB

在调用 malloc_trim 之后,堆被缩小了。我假设 8MB mmap 段仍然可用,因为最后一块内存没有释放。

为什么 malloc 不自动执行堆修剪? 有没有办法配置 malloc 以便自动完成修剪(当它可以节省那么多内存时)?

我使用的是 glibc 2.17 版。

【问题讨论】:

  • 如果您使用大量内存并需要以特定方式处理它,我建议您在 POSIX 上使用 mmap 和在 Windows 上使用 VirtualAlloc 自己完成。

标签: linux malloc free glibc


【解决方案1】:

很大程度上由于历史原因,用于小分配的内存来自使用brk 系统调用管理的池。这是一个非常古老的系统调用——至少和Version 6 Unix 一样古老——它唯一能做的就是改变一个“arena”的大小,它在内存中的位置是固定的。这意味着,brk 池不能缩小到仍然分配的块。

您的程序分配了 N 个内存块,然后释放其中的 N-1 个。它没有释放的一个块是位于最高地址的那个。这是brk 的最坏情况:即使 99.99% 的池未使用,大小也无法减小!如果你改变你的程序,让它没有释放的块是array[0]而不是array[NUM_CHUNKS-1],你应该看到RSS和地址空间在最后调用free时都缩小了。

当您显式调用 malloc_trim 时,它会尝试使用 Linux 扩展 madvise(MADV_DONTNEED) 来解决此限制,该扩展会释放物理 RAM,但不会释放地址空间(如您所见)。我不知道为什么只有在明确调用 malloc_trim 时才会发生这种情况。

顺便说一句,8MB mmap 段是为您初始分配的array

【讨论】:

  • 感谢您的回答!我看到 array 确实位于 8MB 段上,我还看到 array[N-1] 位于堆段的 end 上。如果是在最后,我现在不明白怎么修剪(调用malloc_trim时)。
  • 我明白了,谢谢。有没有办法减少消耗的 RSS 内存量?除了时不时打电话给malloc_trim
  • 我尝试的第一件事是插入jemalloc
  • 更改 malloc 实现对我来说不是一个选项。
  • 嗯,这基本上是 glibc 的malloc 的一个限制,所以如果你从malloptmalloc_trim 获得的控制不够好,那么替换实现几乎是你唯一的选择。我确实在这里向 glibc 开发人员提到了这个限制:sourceware.org/ml/libc-alpha/2016-07/msg00646.html,但这可能不会很快对您有所帮助,特别是如果您坚持使用 2.17(当他们即将发布 2.24 时)。
猜你喜欢
  • 2020-12-15
  • 2012-09-22
  • 2014-03-14
  • 2018-04-04
  • 1970-01-01
  • 1970-01-01
  • 2020-09-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多