【问题标题】:malloc in child thread cost too much virtual memory子线程中的 malloc 占用过多的虚拟内存
【发布时间】:2015-02-07 09:20:49
【问题描述】:
void * thread_client_timeout_check(void *arg)
{
    pthread_attr_t attr;size_t size;
    pthread_attr_init(&attr);
    pthread_attr_getstacksize(&attr, &size);
    printf("pthread stacksize: %d\n", size);
    malloc(1);
}

主线程创建子线程并暂停。

int main()
{
    pthread_t pid;
    pthread_create(&pid, NULL, thread_client_timeout_check, NULL);
    pause();
}
  1. pthread_create之前,top virt0.3m
  2. pthread_create之后,top virtis8.3m(pthread堆栈大小为8m)
  3. malloc(1),top virtis72.3m之后

为什么malloc(1) 会从内核获得54m 虚拟内存?

【问题讨论】:

  • 没有其他线程做任何可能增加内存的事情?
  • 如果 malloc 对每次分配都一直深入到内核,它会很慢。获取大块然后在用户空间中处理它们更有意义。
  • @Mat 你是对的,但是 54m 太大了。我先在主线程中检查malloc(1),虚拟内存仍然是0.3m。
  • 你是怎么得到virt的?有topVIRT 字段?
  • 是的,虚拟内存就是地址空间。几乎免费,尤其是在 64 位架构上。

标签: c linux multithreading glibc


【解决方案1】:

在多线程程序中,glibc 2.10+ 创建了许多 malloc 池以减少错误共享,从而提高可伸缩性。结果是从 glibc 2.10 开始,虚拟内存使用率会高得多。但由于地址空间很便宜,或者在 64 位架构上或多或少是免费的,所以真的没什么好担心的。

https://udrepper.livejournal.com/20948.html

【讨论】:

    【解决方案2】:

    虚拟内存(virt)不是程序分配的内存。相当类似于一种内存足迹(包含数据+代码+常驻+交换内存。它还包含共享库使用的共享代码和数据段)。 Glibc 的 malloc 内存分配对不同的块大小(fastbins、mmap()、sbrk())使用不同的策略,并且小的实际内存使用会导致巨大的 virt 内存。例如。分配 10 次 64 KiB + 1KiB 块并释放较低的 10 x 64KiB 块。实际使用量为 1 KiB,但在 virt 中计算的堆上的内存为 641 KiB(+cca 100 KiB 用于最顶层(所谓的荒野)块),因为堆上释放的内存仍然属于进程的地址空间。

    您可以使用mallinfo(3)查看实际的空闲内存:

    #include <malloc.h>
    
    #define pfld(fld, unit, rem) printf("  %-9s= %d %s, (%s)\n", #fld, mi.fld, #unit, rem)
    
    void showmem(const char *fnc, const char *step) {
        struct mallinfo mi = mallinfo();
    
        printf("\n==== %s: %s ====\n", fnc, step);
    
        pfld(arena,    bytes, "Non-mmapped space allocated");
        pfld(ordblks,  pcs,   "free chunks");
        pfld(smblks,   pcs,   "free fastbin blocks");
        pfld(hblks,    pcs,   "mmapped regions");
        pfld(hblkhd,   bytes, "Space allocated in mmapped regions");
        pfld(usmblks,  bytes, "Maximum total allocated space");
        pfld(fsmblks,  bytes, "Space in freed fastbin blocks");
        pfld(uordblks, bytes, "Total allocated space");
        pfld(fordblks, bytes, "Total free space");
        pfld(keepcost, bytes, "Top-most, releasable space");
    }
    

    从程序的不同部分调用此函数为showmem(__FUNCTION__, "Step"),您可以看到总分配空间和总可用空间。我假设在您的情况下,总可用空间很高,而总分配空间很低。这可能是因为 pthread 库分配和释放的内存。

    您可以进行测试。使用mallopt(3),您可以要求malloc 始终使用mmap(2) 来分配内存,而不是更方便的sbrk(2)。通常mmap(2) 仅用于大于或等于 128 KiB 的块。 Mmap 的内存在 malloc 时总是归零(导致处理开销并总是分配 4KiB 页面),这是由内核提供的,并且在 free 之后总是从进程的地址空间中删除并返回给内核(因此从 free 的指针中读取将导致分段错误)。如果释放 mmap 的内存,它会立即从您的地址空间中删除,因此分配的内存占用空间将立即减少。尝试:malloc 10 x 128 KiB + 1 KiB,然后释放 10 x 128 KiB。 Virt 将减少到仅包含 1 KiB 部分。

    打电话

    mallopt(M_MMAP_THRESHOLD, 0);
    

    在您的程序开始时将强制 glibc 的 malloc 始终使用 mmap(3)。如果我的假设是正确的,那么 virt mem 将会减少。我不建议使用这个(对于小块它会导致很大的内存开销,并且总是用 0 填充内存页面导致 CPU 开销),但理论可以测试。

    这只是 glibc。其他内存管理器(如 tcmalloc)使用不同的策略。

    我希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 2013-06-24
      • 2012-05-14
      • 1970-01-01
      • 2012-10-16
      • 2020-07-09
      • 2012-06-28
      • 1970-01-01
      • 1970-01-01
      • 2023-04-03
      相关资源
      最近更新 更多