【问题标题】:memory overhead of malloc() vs new[]malloc() 与 new[] 的内存开销
【发布时间】:2015-10-11 13:25:13
【问题描述】:

我想保留一块内存 (1GB) 以将数据加载到其中进行分析。每条记录大约 10K 字节,至少有 100k 条记录。最初我打算在 c++ 代码中使用 malloc,但有人建议我不要这样做。

现在,使用 char * block = new char[1000000000] 是否需要额外的内存来存储指向数组中 1,000,000,000 个元素的指针?使用 char * block = malloc(1000000000 * sizeof(char)) 会比 new[] 需要更少的额外内存吗?

我的目标是尽可能少地使用内存,并且不希望将记录淹没在内存中。

谢谢:)

【问题讨论】:

  • 您认为new 实际做了什么?您会惊讶地发现它结合了对 malloc 的调用以分配内存和对构造函数的调用以构建对象(如果最后一部分是相关的)?
  • mallocnew 几乎可以肯定使用相同的底层分配器。为什么建议您不要这样做?
  • malloc() 只是给你一大块内存。 new() 实际上会运行一堆代码给你一个对象,它会在内部做malloc() 无论如何给你和你自己调用 malloc 一样的空间。
  • @Olaf:char 的构造函数是什么?
  • 有什么原因不能使用std::vectorstd::vector::reservestd::vector::resize

标签: c++ memory malloc


【解决方案1】:

在我的 Linux 机器上:

Malloc

//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],它会消失吗?
【解决方案2】:

为了使operator delete[] 能够与非 POD 正常工作,数组的大小(单个 size_t)通常放在整个块的开头,第一个对象在第一个适当对齐的位置地址。

对于 POD,operator new[](没有初始化器)通常与 malloc 相同。

使用初始化程序(同样,使用 POD 类型),结果取决于编译器:它可以转换为元素循环,或减少为 memset

鉴于您打算分配的大量内存,malloc 的结果取决于运行时 - 某些实现对块大小有硬性上限。

如果你的目标是 Windows,你可以使用 VirtualAlloc 来处理这个大小的东西。同样,在 *nix 上使用 mmap

【讨论】:

  • 在带有 gcc 的 Linux 上,它不会以任何方式触及 char 数组的内存。 Memsetting 或迭代它会导致系统内存使用量的实际峰值。使用我的答案中的示例程序,没有峰值。
  • 这是由于操作系统首次分页内存。默认分配 POD 不需要调用构造函数,因此在未初始化的 char 情况下不会发生分页。如果您每 4KiB 读取一个 char,它可能会产生相同的峰值。
【解决方案3】:

你问:

现在,使用 char * block = new char[1000000000] 是否需要额外的内存来存储指向数组中 1,000,000,000 个元素的指针?

绝对不是。

来自 C++11 标准(第 5.3.4 节新增)

5 当分配的对象为数组时(即使用noptr-new-declarator语法或new-type-id类型-id 表示数组类型),new 表达式产生一个指向数组初始元素(如果有的话)的指针。

其中的关键部分是您返回指向数组初始元素(如果有)的指针

你还问过:

new[]相比,使用char * block = malloc(1000000000 * sizeof(char))创建所需的额外内存会更少吗?

该标准没有指定任何与使用任何一种分配方法相关的开销。在大多数实现中,与这两种方法相关的内存开销即使不完全相同也应该大致相同。如果这不是真的,我会感到惊讶。

【讨论】:

    【解决方案4】:

    new[N] 的保留比要求的多一点。它在开头存储计数器 [N](以了解它需要使用 delete[] 调用多少析构函数)并在它之后返回一个内存块。

    【讨论】:

      【解决方案5】:

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

      new 将做的是分配内存,然后调用构造函数,在您的情况下,构造函数不会产生任何后果,因为这只是一个普通旧数据数组。

      我使用带有调试编译的 Visual Studio 2013 进行了快速检查,并查看了 Windows 任务管理器中的内存分配,因为我首先跳过了 new,然后是 malloc(),数字看起来大致相同每一步的内存分配。

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

      【讨论】:

        猜你喜欢
        • 2011-11-19
        • 1970-01-01
        • 2011-02-02
        • 1970-01-01
        • 2016-03-23
        • 1970-01-01
        • 1970-01-01
        • 2018-12-23
        • 2023-03-11
        相关资源
        最近更新 更多