【问题标题】:How does memory on the heap get exhausted?堆上的内存如何耗尽?
【发布时间】:2020-02-10 03:38:41
【问题描述】:

我一直在测试我自己的一些代码,以查看耗尽堆或空闲存储上的内存需要多少分配的内存。但是,除非我的代码在测试中是错误的,否则我会在堆上放置多少内存方面得到完全不同的结果。

我正在测试两个不同的程序。第一个程序在堆上创建向量对象。第二个程序在堆上创建整数对象。

这是我的代码:

#include <vector>
#include <stdio.h>

int main()
{
    long long unsigned bytes = 0;
    unsigned megabytes = 0;

    for (long long unsigned i = 0; ; i++) {

        std::vector<int>* pt1 = new std::vector<int>(100000,10);

        bytes += sizeof(*pt1);
        bytes += pt1->size() * sizeof(pt1->at(0));
        megabytes = bytes / 1000000;

        if (i >= 1000 && i % 1000 == 0) {
            printf("There are %d megabytes on the heap\n", megabytes);
        }

    }
}

在收到bad_alloc 错误之前这段代码的最终输出是:“堆上有 2000 兆字节”

在第二个程序中:

#include <stdio.h>

int main()
{
        long long unsigned bytes = 0;
        unsigned megabytes = 0;

        for (long long unsigned i = 0; ; i++) {

           int* pt1 = new int(10);

           bytes += sizeof(*pt1);
           megabytes = bytes / 1000000;

           if (i >= 100000 && i % 100000 == 0) {
              printf("There are %d megabytes on the heap\n", megabytes);
        }

    }
}

此代码在收到bad_alloc 错误之前的最终输出是:“堆上有 511 兆字节”

两个程序的最终输出大不相同。我对免费商店有什么误解吗?我认为这两个结果会差不多。

【问题讨论】:

  • 不确定,是否相关,但Linux allocates memory lazily
  • @P.Dmitry 没关系:这里分配的数据由10初始化。
  • @Ruslan 基本上是对的,但是编译器可以看到,这个值是未使用的,可以(?)删除这个初始化
  • @P.Dmitry 这发生在调试版本中(刚刚测试过)。我不希望在这种情况下发生这样的优化。
  • FWIW,我还得到第一种和第二种情况(4000 对 1071)之间的大约 4 倍差异。可能是由于管理 4 字节与 100000 字节分配块的开销。

标签: c++ memory-management out-of-memory


【解决方案1】:

首先,您必须了解操作系统分配内存以处理称为页面的相当大的内存块(这是硬件属性)。页面大小约为 4 -16 kB。

现在标准库尝试以有效的方式使用内存。因此,它必须找到一种方法将页面分割成更小的部分并进行管理。为此,必须维护一些有关堆结构的额外信息。

这很酷Andrei Alexandrescu cppcon talk 或多或少是如何工作的(它省略了有关页面管理的信息)。

因此,当您分配大量小对象时,有关堆结构的信息非常大。另一方面,如果您分配较少数量的较大对象,则效率更高 - 跟踪内存结构时占用的内存更少。

另请注意,有时根据堆策略(当请求一小块内存时),浪费一些内存并返回比请求更大的内存更有效。

【讨论】:

    【解决方案2】:

    很有可能pointers returned by new on your platform are 16-byte aligned

    如果 int4 字节,这意味着对于每个 new int(10),您将获得 4 个字节并使 12 个字节不可用。

    仅此一项就可以解释从小分配获得 500MB 可用空间与从大分配获得 2000MB 之间的区别。

    最重要的是,跟踪分配的块(至少是它们的大小以及它们是空闲的还是正在使用的)会产生开销。这非常特定于系统的内存分配器,但也会产生每次分配的开销。有关glibc 分配器的说明,请参阅https://sourceware.org/glibc/wiki/MallocInternals 中的“什么是块”。

    【讨论】:

    • malloc 分配 16 字节的块(并考虑到它们)仍然给我留下了大约 2 倍的差异:2142 MB 与 4000 MB。
    猜你喜欢
    • 2012-06-30
    • 2023-03-08
    • 1970-01-01
    • 2015-08-23
    • 2012-11-10
    • 2012-08-19
    • 2011-02-09
    • 2015-10-01
    • 2018-12-11
    相关资源
    最近更新 更多