【问题标题】:Why memset function make the virtual memory so large为什么memset函数让虚拟内存这么大
【发布时间】:2013-09-04 12:38:55
【问题描述】:

我有一个进程会做很多光刻计算,所以我使用 mmap 为内存池分配一些内存。当进程需要大块内存时,我使用mmap分配一个块,用完后放入内存池,如果进程再次需要相同的块内存,直接从池中获取,不使用内存再次映射。(不是分配所有需要的内存并在进程开始时将其放入池中)。在 mmaps 函数之间,还有一些内存 malloc 没有用到 mmap,比如 malloc() 或 new()。

现在的问题是: 如果我在放入内存池之前使用 memset() 将所有块数据设置为零,则该进程将使用过多的虚拟内存,格式为“mmap(size)=virtual address”:

mmap(4198400)=0x2aaab4007000
mmap(4198400)=0x2aaab940c000
mmap(8392704)=0x2aaabd80f000
mmap(8392704)=0x2aaad6883000
mmap(67112960)=0x2aaad7084000
mmap(8392704)=0x2aaadb085000
mmap(2101248)=0x2aaadb886000
mmap(8392704)=0x2aaadba89000
mmap(67112960)=0x2aaadc28a000
mmap(2101248)=0x2aaae028b000
mmap(2101248)=0x2aaae0c8d000
mmap(2101248)=0x2aaae0e8e000
mmap(8392704)=0x2aaae108f000
mmap(8392704)=0x2aaae1890000
mmap(4198400)=0x2aaae2091000
mmap(4198400)=0x2aaae6494000
mmap(8392704)=0x2aaaea897000
mmap(8392704)=0x2aaaeb098000
mmap(2101248)=0x2aaaeb899000
mmap(8392704)=0x2aaaeba9a000
mmap(2101248)=0x2aaaeca9c000
mmap(8392704)=0x2aaaec29b000
mmap(8392704)=0x2aaaecc9d000
mmap(2101248)=0x2aaaed49e000
mmap(8392704)=0x2aaafd6a7000
mmap(2101248)=0x2aacc5f8c000

mmap last - first = 0x2aacc5f8c000 - 0x2aaab4007000 = 8.28G

但是如果我在放入内存池之前不调用 memset:

mmap(4198400)=0x2aaab4007000
mmap(8392704)=0x2aaab940c000
mmap(8392704)=0x2aaad2480000
mmap(67112960)=0x2aaad2c81000
mmap(2101248)=0x2aaad6c82000
mmap(4198400)=0x2aaad6e83000
mmap(8392704)=0x2aaadb288000
mmap(8392704)=0x2aaadba89000
mmap(67112960)=0x2aaadc28a000
mmap(2101248)=0x2aaae0a8c000
mmap(2101248)=0x2aaae0c8d000
mmap(2101248)=0x2aaae0e8e000
mmap(8392704)=0x2aaae1890000
mmap(8392704)=0x2aaae108f000
mmap(4198400)=0x2aaae2091000
mmap(4198400)=0x2aaae6494000
mmap(8392704)=0x2aaaea897000
mmap(8392704)=0x2aaaeb098000
mmap(2101248)=0x2aaaeb899000
mmap(8392704)=0x2aaaeba9a000
mmap(2101248)=0x2aaaec29b000
mmap(8392704)=0x2aaaec49c000
mmap(8392704)=0x2aaaecc9d000
mmap(2101248)=0x2aaaed49e000

mmap last - first = 0x2aaaed49e000 - 0x2aaab4007000= 916M

所以第一个进程将“内存不足”并被杀死。

在这个过程中,mmap内存块虽然被分配了,但不会被完全使用或者甚至不会被使用,我的意思是,例如在校准之前,进程mmap 67112960(64M),它不会被使用(写入或读取)此内存区域中的数据)或仅使用前 2M 字节,然后放入内存池。

我知道mmap只是返回虚拟地址,物理内存使用延迟分配,读取或写入这些地址时会分配。

但让我困惑的是,为什么虚拟地址增加了这么多?我使用的是 centos 5.3,内核版本是 2.6.18,我在 libhoard 和 GLIBC(ptmalloc) 上都尝试了这个过程,两者的行为相同。

以前有没有人遇到过同样的问题,可能的根本原因是什么?

谢谢。

【问题讨论】:

    标签: memory-management linux-kernel virtual-memory memset


    【解决方案1】:

    VMA(虚拟内存区域,AKA 内存映射)不需要是连续的。您的第一个示例使用 ~256 Mb,第二个使用 ~246 Mb。

    常见的malloc() 实现自动使用mmap() 进行大分配(通常大于64Kb),使用munmap() 释放相应的块。因此,您无需手动 mmap() 进行大量分配,您的 malloc() 库会处理这些问题。

    mmap()ing 时,内核返回一个特殊零页的 COW 副本,因此在写入之前它不会分配内存。您的归零导致内存被真正分配,最好将其返回给分配器,并在需要时请求新的内存块。

    结论:不要编写自己的内存管理,除非系统已证明不能满足您的需求,然后才使用您自己的内存管理,当您证明它明显更好地满足您的实际负载需求时。

    【讨论】:

    • 进程会做上千次相同的分配,所以我们使用内存池,这样会节省很多时间,因为虽然mmap不会做物理内存分配,但是真的太费了每次分配都有很多时间。
    猜你喜欢
    • 2021-07-17
    • 1970-01-01
    • 1970-01-01
    • 2012-05-01
    • 2014-02-12
    • 1970-01-01
    • 1970-01-01
    • 2013-10-21
    • 1970-01-01
    相关资源
    最近更新 更多