为什么 malloc 会消耗那么多内存?
Why malloc consume that much memory?
我有一个项目,我在其中做了很多 malloc。我发现内存使用量比数据本身大得多。如果我使用 valgrind 并放入 100 MB 数据,则分配的内存为 500 MB。数据块大小不同,每个 20-40 字节。这是做类似事情的最小程序,但块大小相同。
它分配了大约 43 MB,但 valgrind massif 显示 53 MB。
如果 运行 使用 jemalloc,顶部也会显示 47 MB。
目前所有块的大小都不同,我不能使用数组或其他东西。
是否有一些我可以使用的 malloc 设置,或者是否有不同的类似 malloc 的命令可以用来最大限度地减少内存浪费?
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define BUFFER_SIZE 39
#define MANY 1000000LU
typedef struct _list{
void *next;
char payload[BUFFER_SIZE];
}list;
int main(){
list root;
printf("Allocating %lu chunks %zu bytes each, equals to %lu bytes\n", MANY, sizeof(list), MANY * sizeof(list));
list *node = & root;
unsigned long int i;
for(i = 0; i < MANY; i++){
node->next = malloc(sizeof(list));
if (node->next == NULL){
printf("Out of memory\n");
return 1;
}
memset(node->payload, 0, BUFFER_SIZE);
node = node->next;
}
printf("done\n");
return 0;
}
首先,开销。 malloc
结构中有一个小的开销。我认为每个 malloc
大约有 8 个字节。此外(部分是为了对齐,部分是为了实用性,部分是为了最大限度地减少下面的影响),内存分配的大小将被四舍五入为 2 的小幂的倍数。
其次,碎片化。假设您分配了 3 900 字节的块,A、B 和 C,分配器选择按顺序分配它们。接下来,假设 B 被释放。由于空洞小于 OS 页面大小,B 所在的 'hole' 无法返回给 OS。如果使用 malloc
的所有后续分配都大于 900 字节,则永远不会填充该漏洞 - 即它被浪费了。如果(比方说)出现 600 字节的分配,它 可能 被放置在空洞中,但这将留下 300 字节的空洞。在这种情况下,没有分配器能够完美地执行。
在现代 OS 上,malloc
的常见实现将请求大块虚拟内存 space 并将其分成分配池。然后它将根据自己的优化参数(无论是大小、线程、碎片减少等)从这些池中分配内存。天真的记忆观认为这是浪费。然而,虚拟内存在投入使用之前不一定由物理页面支持,这时 OS 将按需为其回填映射(类似于分页的工作方式,但使用匿名内存支持)。因此,您认为进程正在使用的数量不一定是正在消耗的实际内存,只是分配的 VM space 等待使用。
我有一个项目,我在其中做了很多 malloc。我发现内存使用量比数据本身大得多。如果我使用 valgrind 并放入 100 MB 数据,则分配的内存为 500 MB。数据块大小不同,每个 20-40 字节。这是做类似事情的最小程序,但块大小相同。
它分配了大约 43 MB,但 valgrind massif 显示 53 MB。
如果 运行 使用 jemalloc,顶部也会显示 47 MB。
目前所有块的大小都不同,我不能使用数组或其他东西。
是否有一些我可以使用的 malloc 设置,或者是否有不同的类似 malloc 的命令可以用来最大限度地减少内存浪费?
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define BUFFER_SIZE 39
#define MANY 1000000LU
typedef struct _list{
void *next;
char payload[BUFFER_SIZE];
}list;
int main(){
list root;
printf("Allocating %lu chunks %zu bytes each, equals to %lu bytes\n", MANY, sizeof(list), MANY * sizeof(list));
list *node = & root;
unsigned long int i;
for(i = 0; i < MANY; i++){
node->next = malloc(sizeof(list));
if (node->next == NULL){
printf("Out of memory\n");
return 1;
}
memset(node->payload, 0, BUFFER_SIZE);
node = node->next;
}
printf("done\n");
return 0;
}
首先,开销。 malloc
结构中有一个小的开销。我认为每个 malloc
大约有 8 个字节。此外(部分是为了对齐,部分是为了实用性,部分是为了最大限度地减少下面的影响),内存分配的大小将被四舍五入为 2 的小幂的倍数。
其次,碎片化。假设您分配了 3 900 字节的块,A、B 和 C,分配器选择按顺序分配它们。接下来,假设 B 被释放。由于空洞小于 OS 页面大小,B 所在的 'hole' 无法返回给 OS。如果使用 malloc
的所有后续分配都大于 900 字节,则永远不会填充该漏洞 - 即它被浪费了。如果(比方说)出现 600 字节的分配,它 可能 被放置在空洞中,但这将留下 300 字节的空洞。在这种情况下,没有分配器能够完美地执行。
在现代 OS 上,malloc
的常见实现将请求大块虚拟内存 space 并将其分成分配池。然后它将根据自己的优化参数(无论是大小、线程、碎片减少等)从这些池中分配内存。天真的记忆观认为这是浪费。然而,虚拟内存在投入使用之前不一定由物理页面支持,这时 OS 将按需为其回填映射(类似于分页的工作方式,但使用匿名内存支持)。因此,您认为进程正在使用的数量不一定是正在消耗的实际内存,只是分配的 VM space 等待使用。