Joh*_*y V 4 c memory memory-management
我有一个网络应用程序,它将可预测的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
Run Code Online (Sandbox Code Playgroud)
注意事项:
total in use bytes根据我的内部计数器,这是完全正确的数字。但是,该应用程序的RES(从顶部/顶部)为5.2GB。分配几乎总是65k;我不明白在mmap发挥作用时,我看到的更多碎片/废料甚至更多。total system bytes不等于system bytes每个竞技场的总和。如何保留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
Run Code Online (Sandbox Code Playgroud)
我的假设如下:所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可以选择连续块是自由/当时可用或合并不是。
如果块足够小,它什么都不做更多(IE)的块是可用于重新分配,但是,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中”。
要告诉内核不太可能很快使用内存区域,我们需要使用madvisesyscall MADV_WONTNEED。这告诉内核,内存区域的优先级较低,它应[更多]积极地将其分页到分页磁盘,从而减少进程的RSS。
页面仍映射在进程的虚拟地址空间中,但被导出到分页磁盘。请记住,页面映射为不同比页面居住。
如果该进程再次访问该页面,则将导致页面错误,内核将把数据从页面磁盘拉到物理RAM插槽并重新映射。RSS回去。古典需求分页。
madvise是malloc_trim用来减少过程的RSS的东西。
| 归档时间: |
|
| 查看次数: |
996 次 |
| 最近记录: |