malloc()与new[]的内存开销比较

3
我希望预留一块内存(1GB)用于加载数据以进行分析。每个记录的大小约为10K字节,至少有100k条记录。最初我打算在c++代码中使用malloc,但是有人建议我不要这样做。
现在,使用char * block = new char[1000000000]是否需要额外的内存来存储数组中每个1,000,000,000个元素的指针?使用char * block = malloc(1000000000 * sizeof(char))创建时需要比new[]更少的额外内存吗?
我的目标是尽可能少地使用内存,并且不想在内存中打入和打出记录。
谢谢 :)

5
那么,你认为 new 实际上是做什么的?如果告诉你它会结合调用 malloc 来分配内存和调用构造函数来构建对象(如果后面一部分相关),你会感到惊讶吗? - Serge Ballesta
4
“malloc”和“new”几乎肯定使用相同的基础内存分配器。为什么你被建议不要这样做? - Jonathan Potter
1
malloc()只是给你一块内存。new()将实际运行一堆代码来为您提供一个对象,该对象将在内部执行malloc()以向您提供与自己调用malloc相同的空间。 - Marc B
2
@Olaf:char的构造函数是什么? - Steve Summit
2
你不能使用 std::vectorstd::vector::reservestd::vector::resize 的原因是什么? - Daniel
显示剩余4条评论
5个回答

3

在我的Linux机器上:

动态内存分配

//malloc.cc
#include <cstdlib>
int main() { char* block = (char*) malloc(1000000000); }

运行时:

$ make malloc
$ valgrind ./malloc 2>&1|grep total
==23855==   total heap usage: 1 allocs, 0 frees, 1,000,000,000 bytes allocated

新的

//new.cc
int main() { char* block = new char[1000000000]; }

运行时:

$ make new
$ valgrind ./new 2>&1|grep total
  ==24460==   total heap usage: 2 allocs, 0 frees, 1,000,072,704 bytes allocated

72,704B的开销不会因为不同的数值而改变。

有趣。我想知道使用 new[] 时第二个分配是用来干什么的——也许它与异常处理有关,如果 new 失败了呢?如果你改成 new (std::nothrow) char[1000000000],会怎样呢? - Cameron

2
为了使operator delete[]与非POD类型正常工作,通常将数组的大小(一个size_t)放置在整个块的开头,并将第一个对象放置在第一个适当对齐的地址。
对于POD类型,operator new[](没有初始化器)通常与malloc相同。
使用初始化器(再次使用POD类型),结果取决于编译器:它可能会转换为对元素的循环,或者简化为memset
考虑到您打算分配的大量内存,malloc的结果取决于运行时-某些实现对块大小有硬上限。
如果您的目标是Windows,则可以针对此大小使用VirtualAlloc。同样,在*nix上使用mmap

在Linux上使用gcc编译时,它不会以任何方式触及char数组的内存。对其进行memset或迭代将导致系统内存使用量实际上出现峰值。使用我回答中的示例程序,就不会出现这种情况。 - Petr Skocik
这是由于操作系统首次分页内存所致。默认分配POD不需要构造函数调用,因此在未初始化的char情况下没有发生分页。如果您每4KiB读取一个单独的char,可能会产生相同的峰值。 - defube

1
您问道:
现在,使用char * block = new char [1000000000]需要额外的内存来存储数组中每个1,000,000,000个元素的指针吗?
当然不需要。
从C++11标准(第5.3.4节New)中可以看出:
当分配的对象是数组时(即使用noptr-new-declarator语法或new-type-id或type-id表示数组类型),新表达式将产生指向数组的初始元素(如果有)的指针。
其中关键的一点是,您会得到指向数组初始元素的指针(如果有)。
您还问道:
使用char * block = malloc(1000000000 * sizeof(char))创建需要比new[]需要更少的附加内存吗?
标准并未具体说明使用任何一种分配方法所涉及的开销。在大多数实现中,如果不是完全相同,那么这两种方法所涉及的内存开销应该大致相同。如果这不是真的,我会感到惊讶。

0

new[N] 会预留比所需稍微多一点的内存。它在开头存储计数器 [N](以便知道需要使用 delete[] 调用多少个析构函数),并返回其后的内存块。


0

如果您使用new来分配字符数组,你将会得到一个字符数组。每个元素都不会有额外的指针。您只会得到一片连续的内存区域,类似于使用malloc()

new将分配内存,然后调用构造函数。在这种情况下,您的构造函数不会做任何有意义的事情,因为这只是一个普通旧数据(Plain Old Data)的数组。

我使用Visual Studio 2013进行了快速检查,进行了调试编译,并在Windows任务管理器中查看了内存分配情况。当我逐步跨越首先new,然后再malloc()时,每个步骤的内存分配量看起来大致相同。

由于这么大的内存区域,您可能会遇到页面错误,因为操作系统会在不同部分访问内存区域时将其分页。不确定您是否真的可以对此采取任何措施,也不确定这是否是一个大问题。任何交换行为都将取决于您所拥有的物理内存量以及其他服务和应用程序以及它们的内存使用情况的混合。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接