gpt4 book ai didi

c - Malloc使用的内存是所需内存的10倍

转载 作者:行者123 更新时间:2023-12-03 20:22:49 25 4
gpt4 key购买 nike

我有一个网络应用程序,它将可预测的65k块分配为IO子系统的一部分。内存使用情况是在系统内自动跟踪的,因此我知道我实际使用了多少内存。也可以根据malloc_stats()检查此数字

malloc_stats()的结果

Arena 0:
system bytes = 1617920
in use bytes = 1007840
Arena 1:
system bytes = 2391826432
in use bytes = 247265696
Arena 2:
system bytes = 2696175616
in use bytes = 279997648
Arena 3:
system bytes = 6180864
in use bytes = 6113920
Arena 4:
system bytes = 16199680
in use bytes = 699552
Arena 5:
system bytes = 22151168
in use bytes = 899440
Arena 6:
system bytes = 8765440
in use bytes = 910736
Arena 7:
system bytes = 16445440
in use bytes = 11785872
Total (incl. mmap):
system bytes = 935473152
in use bytes = 619758592
max mmap regions = 32
max mmap bytes = 72957952


注意事项:


根据我的内部计数器, total in use bytes是完全正确的数字。但是,该应用程序的RES(从顶部/顶部)为5.2GB。分配几乎总是65k;我不明白在mmap发挥作用时,我看到的更多碎片/废料甚至更多。
total system bytes不等于每个竞技场中 system bytes的总和。
我在使用glibc 2.23-0ubuntu3的Ubuntu 16.04上
Arena 1和Arena 2导致内核报告的RES值很大。
1号竞技场和2号竞技场将使用的内存量提高了10倍。
大部分分配始终为65k(页面大小的显式倍数)


如何保留malloc来分配荒谬的内存?

我认为此版本的malloc有一个巨大的错误。最终(一个小时后)将释放一半以上的内存。这不是致命的错误,但绝对是一个问题。

更新-我添加了 mallinfo并重新运行测试-捕获此应用时,该应用程序不再处理任何内容。没有连接网络。它是空闲的。

Arena 2:
system bytes = 2548473856
in use bytes = 3088112
Arena 3:
system bytes = 3288600576
in use bytes = 6706544
Arena 4:
system bytes = 16183296
in use bytes = 914672
Arena 5:
system bytes = 24027136
in use bytes = 911760
Arena 6:
system bytes = 15110144
in use bytes = 643168
Arena 7:
system bytes = 16621568
in use bytes = 11968016
Total (incl. mmap):
system bytes = 1688858624
in use bytes = 98154448
max mmap regions = 32
max mmap bytes = 73338880
arena (total amount of memory allocated other than mmap) = 1617780736
ordblks (number of ordinary non-fastbin free blocks) = 1854
smblks (number of fastbin free blocks) = 21
hblks (number of blocks currently allocated using mmap) = 31
hblkhd (number of bytes in blocks currently allocated using mmap) = 71077888
usmblks (highwater mark for allocated space) = 0
fsmblks (total number of bytes in fastbin free blocks) = 1280
uordblks (total number of bytes used by in-use allocations) = 27076560
fordblks (total number of bytes in free blocks) = 1590704176
keepcost (total amount of releaseable free space at the top of the heap) = 439216


我的假设如下: total system bytes报告的 malloc之间的差异远小于每个 arena报告的数额。 (1.6Gb vs 6.1GB)这可能意味着(A) malloc实际上正在释放块,但竞技场却没有,或者(B) malloc根本没有压缩内存分配,并且正在创建大量的碎片。

UPDATE Ubuntu发布了一个内核更新,它基本上修复了本文中所述的所有内容。就是说,这里有很多关于malloc如何与内核一起工作的好信息。

最佳答案

完整的细节可能会有些复杂,因此我将尽量简化。另外,这是一个粗略的轮廓,并且在某些地方可能不准确。



从内核请求内存

malloc使用sbrk或匿名mmap向内核请求连续的内存区域。每个区域将是机器页面大小的倍数,通常为4096字节。这样的存储区域在malloc术语中称为舞台。下面的更多内容。

这样映射的任何页面都将成为进程的虚拟地址空间的一部分。但是,即使已将它们映射到,它们也可能尚未由物理RAM页面备份。在R / O模式下,它们被[多对一]映射到单个“零”页面。

当进程尝试写入此类页面时,会引发保护错误,内核会中断到零页面的映射,分配实际的物理页面,然后重新映射到该页面,然后在故障点重新启动该过程。这次写入成功。这类似于到/从分页磁盘的按需分页。

换句话说,进程的虚拟地址空间中的页面映射与物理RAM页面/插槽中的页面驻留不同。稍后再详细介绍。



RSS(居民集大小)

RSS并不能真正衡量一个进程分配或释放多少内存,而是目前虚拟地址空间中有多少页在RAM中具有物理页。

如果系统的分页磁盘为128GB,但仅具有(例如)4GB RAM,则进程RSS永远不能超过4GB。进程的RSS根据其虚拟地址空间中的页面调入或调出页面而上升/下降。

因此,由于启动时页面映射为零,因此进程RSS可能比其从系统请求的虚拟内存量低得多。同样,如果另一个进程B从给定的进程A“窃取”一个页面槽,则A的RSS下降,B的上升。

进程“工作集”是内核必须为进程保留的最小页数,以基于某种“过分”的措施来防止进程因页面错误而过度获取物理内存页。每个操作系统对此都有自己的想法,并且通常是整个系统或每个进程的可调参数。

如果一个进程分配了一个3GB的阵列,但仅访问它的前10MB,则它的工作集要比随机/分散访问该阵列的所有部分时要低。

也就是说,如果RSS高于(或可以高于)工作集,则该过程将运行良好。如果RSS低于工作集,则该过程将出现过多的页面错误。这可能是因为它的“参考位置”较差,或者是由于系统中的其他事件合谋“窃取”了该进程的页面位置。



malloc和竞技场

为了减少碎片,malloc使用了多个竞技场。每个竞技场都有一个“首选”分配大小(又称“块”大小)。也就是说,较小的请求(例如malloc(32))来自(例如)竞技场A,而较大的请求(例如malloc(1024 * 1024))来自不同的领域(例如)竞技场B。

这样可以防止小分配“烧录”竞技场B中最后一个可用块的前32个字节,从而使其太短而无法满足下一个malloc(1M)

当然,对于每个请求的大小,我们都不能有一个单独的区域,因此“首选”块大小通常为2的幂。

当为给定的块大小创建一个新的竞技场时,malloc不仅会请求块大小的区域,而且还会请求其一部分倍数。这样做是为了可以快速满足相同大小的后续请求,而不必对每个请求都执行mmap。由于最小大小为4096,因此竞技场A将具有4096/32个块或128个块。



免费和munmap

当应用程序执行free(ptr) [ptr代表块]时,该块被标记为可用。 free可以选择合并当时空闲/不可用的连续块。

如果该块足够小,则它什么也不会做(即)该块可用于重新分配,但是free不会尝试将块释放回内核。对于较大的分配,free将[尝试]立即执行munmap

munmap可以取消映射单个页面[甚至是少量字节],即使它位于多页区域的中间。如果是这样,则应用程序现在在映射中有一个“洞”。



malloc_trim和madvise

如果调用free,则可能会调用munmap。如果未映射整个页面,则该过程的RSS(例如A)关闭。

但是,请考虑仍分配的块,或标记为可用/可用但未映射的块。

它们仍然是流程A的RSS的一部分。如果另一个进程(例如B)开始进行大量分配,则系统可能必须将某些进程A的插槽分页到分页盘上(减少A的RSS),以便为B腾出空间(其RSS上升)。

但是,如果没有进程B窃取A的页面槽,则进程A的RSS可以保持较高水平。假设进程A分配了100MB,前一阵子用了,但是现在只使用1MB,RSS仍为100MB。

那是因为没有进程B的“干扰”,内核没有理由从A窃取任何页面槽,因此它们“保留在RSS中”。

要告诉内核不太可能很快使用内存区域,我们需要带有madviseMADV_WONTNEED syscall。这告诉内核内存区域的优先级较低,它应该[更多]积极地将其分页到分页磁盘,从而减少进程的RSS。

页面保持映射在进程的虚拟地址空间中,但被导出到分页磁盘。请记住,页面映射与页面驻留不同。

如果该进程再次访问该页面,则将导致页面错误,内核将把数据从页面磁盘拉到物理RAM插槽并重新映射。 RSS回去。古典需求分页。

madvisemalloc_trim用于减少该过程的RSS的东西。

关于c - Malloc使用的内存是所需内存的10倍,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39753265/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com