为啥 malloc 在我达到某个阈值之前不分配内存?

Posted

技术标签:

【中文标题】为啥 malloc 在我达到某个阈值之前不分配内存?【英文标题】:Why malloc doesn't allocate memory until I hit a certain threshold?为什么 malloc 在我达到某个阈值之前不分配内存? 【发布时间】:2020-11-22 10:49:30 【问题描述】:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])

        size_t sz = atol(argv[1]);
        char *arr = malloc(sz);

        sleep(10);

我编译了这段代码并尝试运行,使用pmap查看程序的内存映射。

当我使用像1024000 这样的大数字时,我会得到这样的映射:

3901:   ./alloc_program 1024000
0000560192f43000      4K r---- alloc_program
0000560192f44000      4K r-x-- alloc_program
0000560192f45000      4K r---- alloc_program
0000560192f46000      4K r---- alloc_program
0000560192f47000      4K rw--- alloc_program
0000560192fac000    132K rw---   [ anon ]
00007f75b69e9000   1004K rw---   [ anon ]     <---- I believe this is the allocated memory
00007f75b6ae4000    148K r---- libc-2.31.so
00007f75b6b09000   1504K r-x-- libc-2.31.so
00007f75b6c81000    296K r---- libc-2.31.so
00007f75b6ccb000      4K ----- libc-2.31.so
00007f75b6ccc000     12K r---- libc-2.31.so
00007f75b6ccf000     12K rw--- libc-2.31.so
00007f75b6cd2000     24K rw---   [ anon ]
00007f75b6ce7000      4K r---- ld-2.31.so
00007f75b6ce8000    140K r-x-- ld-2.31.so
00007f75b6d0b000     32K r---- ld-2.31.so
00007f75b6d14000      4K r---- ld-2.31.so
00007f75b6d15000      4K rw--- ld-2.31.so
00007f75b6d16000      4K rw---   [ anon ]
00007ffe2b26e000    132K rw---   [ stack ]
00007ffe2b318000     12K r----   [ anon ]
00007ffe2b31b000      4K r-x--   [ anon ]
ffffffffff600000      4K --x--   [ anon ]
 total             3496K

我认为标记的行是 malloc 分配的内存(也许我错了)。 但是当我使用像10240 这样的小数字时,我看不到任何分配:

3879:   ./alloc_program 10240
000055e428e26000      4K r---- alloc_program
000055e428e27000      4K r-x-- alloc_program
000055e428e28000      4K r---- alloc_program
000055e428e29000      4K r---- alloc_program
000055e428e2a000      4K rw--- alloc_program
000055e42a257000    132K rw---   [ anon ]
00007f102332c000    148K r---- libc-2.31.so
00007f1023351000   1504K r-x-- libc-2.31.so
00007f10234c9000    296K r---- libc-2.31.so
00007f1023513000      4K ----- libc-2.31.so
00007f1023514000     12K r---- libc-2.31.so
00007f1023517000     12K rw--- libc-2.31.so
00007f102351a000     24K rw---   [ anon ]
00007f102352f000      4K r---- ld-2.31.so
00007f1023530000    140K r-x-- ld-2.31.so
00007f1023553000     32K r---- ld-2.31.so
00007f102355c000      4K r---- ld-2.31.so
00007f102355d000      4K rw--- ld-2.31.so
00007f102355e000      4K rw---   [ anon ]
00007fff1d513000    132K rw---   [ stack ]
00007fff1d570000     12K r----   [ anon ]
00007fff1d573000      4K r-x--   [ anon ]
ffffffffff600000      4K --x--   [ anon ]
 total             2492K

1 - 为什么内存较小时不分配?

2 - 为什么分配的内存大小不完全相同?在第一次运行中,它显示大小为1004KB,而我只分配了1000KB

【问题讨论】:

【参考方案1】:

1 - 为什么内存较小时不分配?

函数malloc 的任务是在应用程序需要时为应用程序提供内存。从理论上讲,malloc 可以按照您的建议将所有内存分配请求转发给操作系统的kernel,以便它仅充当内核内存分配器的包装器。但是,这样做有以下缺点:

    内核一次只提供大量内存,至少有page的内存,也就是根据操作系统的配置,一般至少4096字节。因此,如果应用程序只要求 10 字节的内存,则会浪费大量内存。 System calls 在 CPU 性能方面很昂贵。

由于这些原因,malloc 不直接将内存分配请求转发给内核,而是充当应用程序的内存分配请求和内核之间的中介,效率更高。它向内核请求大量内存,以便满足应用程序的许多较小的内存分配请求。

因此,只有在一次请求大量内存时,malloc 才会将该内存分配请求转发给内核。


2 - 为什么分配的内存大小不完全相同?在第一次运行中,它显示大小为1004KB,而我只分配了1000KB

malloc 分配器必须跟踪它授予应用程序的所有内存分配,并跟踪它已由内核授予的所有内存分配。为了存储这些信息,它需要一些额外的内存空间。这种额外的空间称为“开销”。

【讨论】:

感谢您的回答。您所说的是否暗示分配的区域将始终是页面大小的倍数(在本例中为 4096)? @StackExchange123:是的,所有内核内存分配的粒度都是内存页。因此,如果这是内存页面大小,所有内核内存分配都是 4096 的倍数。 @StackExchange123:如果您使用的是 GNU C 库,那么您可以使用函数 mallinfo 并读取 hblkhd 结构成员来获取内核内存分配的总大小。跨度> @StackExchange123:如果你想知道最初的竞技场在哪里,那么我建议你运行printf( "%p", malloc( 8 ) );并将显示的地址与pmap显示的地图进行比较。 @StackExchange123:如果您想了解有关 GNU C 库的内存分配器的更多信息,我建议您阅读this page。该页面还包含有关分配器内部的更多信息的链接。【参考方案2】:

您在pmap 输出中看到的几乎可以肯定是malloc arena 满足更大请求所需的添加,而不是任何单个 请求。

arena 是分配分配的内存池,很有可能从某个大小开始,并且仅按需扩展。

例如,如果初始 arena 为 1000K,则任何未耗尽的分配都无需获得额外的 arena 空间。如果您确实耗尽了该空间,该进程将尝试从底层环境请求更多的竞技场,以便满足额外的需求。


至于为什么尺寸不符合您的要求,有(至少)两个可能的原因。首先,arena 不仅仅是为您的目的分配的内存,它还包含控制信息,以便可以正确管理内存(大小、校验和、指针、空闲列表等)。

其次,malloc 可能会过度分配,因为期望这不会是耗尽当前竞技场的最后一个请求。一些内存分配策略甚至在请求更多时将当前竞技场大小加倍,以分摊这样做的成本。

【讨论】:

感谢您的回答。有没有办法知道最初的竞技场大小?另外,pmap 的输出中的初始竞技场显示在哪里?

以上是关于为啥 malloc 在我达到某个阈值之前不分配内存?的主要内容,如果未能解决你的问题,请参考以下文章

malloc() 和 free() 在哪里存储分配的大小和地址?

为啥在 C 中需要使用 malloc 进行动态内存分配?

为啥尽管我在变量中使用 malloc 分配更多内存,但当我打印变量的大小时,它仍然显示更少的内存/字节? [复制]

C语言中已经有了malloc和free,为啥还需要new和delete?

C语言中的malloc函数的使用?

没有malloc的c中的动态内存分配