我了解到,在 Linux 上,glibc 的 malloc() 使用 mmap() 来处理非常大的内存块,而 brk() 用于小分配。然而,当我用测试程序对该理论进行实验时,情况似乎并非如此。
首先,我在代码中使用“for”循环来分配由条件 Option1==TRUE 的数组实现的一大块内存 (80MB),并且每次分配都是页面大小的倍数。当 Option2==TRUE 时,内存块将被完全释放。注意 Option1 和 Option2 可以从我的进程运行的命令行进行设置。
观察结果:当选项 1 满足时,我的进程的 RSS 值增加了 80MB。之后,我将 Option2 设置为 TRUE(而 Option1 不满意),然后从 RSS 值中减少了大约相同数量的内存(返回给操作系统)。到目前为止一切都很好。
之后,我修改了“for”循环来分配二大块内存(每个 80MB)由以下实现块和块1与选项1。在每个循环中,两个数组的相同索引被分配相同数量的内存,并且每次分配都是较小超过一页。内存块为块当Option2==TRUE时将被完全释放。和内存块块1当Option3==TRUE时将被完全释放。
观察:当 Option1==TRUE 时,我的进程的 RSS 值增加了 160MB,并且在 Option2==TRUE 后该值保持不变。并且在 Option3==TRUE 之后也保持不变。我使用 Valgrind Massif 工具检查内存使用情况,它报告“for”循环中的两个 malloc() 函数映射了大约 160 MB 的页面。
我可以理解,小分配的内存块保留在进程中,以便它们可以用于将来的分配。但是这些小分配不应该由 brk() 而不是 mmap() 实现吗?是因为 valgrind 没有提供准确的报告,还是因为还有其他一些情况使用 mmap() 来实现 malloc() ?
我的第二个测试的代码复制如下:
char *chunk[200000];
char *chunk1[200000];
int i = 0;
int j,k;
......
if (<Option1 == TRUE>) {
if (i < 100) {
for (k=0; k<2000; k++) {
chunk[i*2000+k] = (char *)malloc(400);
chunk1[i*2000+k] = (char *)malloc(400);
memset (chunk[i*2000+k], 0, 400);
memset (chunk1[i*2000+k], 0, 400);
}
}
}
if (<Option2 == TRUE>) {
for (j = 0; j < 200000; j++) {
if (chunk[j] != NULL) {
free(chunk[j]);
chunk[j] = NULL;
}
}
}
if (<Option3 == TRUE>) {
for (j = 0; j < 200000; j++) {
if (chunk1[j] != NULL) {
free(chunk1[j]);
chunk1[j] = NULL;
}
}
}
......
答案1
这实际上取决于malloc
实施。它可能会预先分配一个更大的连续内存块,然后在其上做一些魔法 - 例如,根据请求的大小从大块的不同部分分配较小的内存块。这么大的块当然可以通过分配mmap
。
您可以检查例如杰马洛克的来源来了解这是如何工作的。