堆上的内存是如何耗尽的?
How does memory on the heap get exhausted?
我一直在测试我自己的一些代码,看看需要多少分配的内存才能耗尽堆或空闲存储上的内存。但是,除非我的代码在测试时出错,否则在堆上可以放置多少内存方面,我会得到完全不同的结果。
我正在测试两个不同的程序。第一个程序在堆上创建向量对象。第二个程序在堆上创建整数对象。
这是我的代码:
#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
错误之前的最终输出是:"There are 2000 megabytes on the heap"
在第二个程序中:
#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
错误之前的最终输出是:"There are 511 megabytes on the heap"
两个程序的最终输出大不相同。我对免费商店有什么误解吗?我以为两个结果差不多。
很有可能pointers returned by new
on your platform are 16-byte aligned.
如果 int
是 4
字节,这意味着对于每个 new int(10)
,您将获得四个字节并使 12 个字节不可用。
仅此一项就可以解释从小分配中获得 500MB 可用 space 和从大分配中获得 2000MB 之间的区别。
除此之外,还有跟踪分配块的开销(至少,它们的大小以及它们是否空闲或正在使用)。这非常特定于您系统的内存分配器,但也会产生每次分配的开销。有关 glibc
分配器的解释,请参阅 https://sourceware.org/glibc/wiki/MallocInternals 中的 "What is a Chunk"。
首先,您必须了解操作系统分配内存以在称为页面的相当大的内存块中进行处理(它是硬件 属性)。页面大小约为 4 -16 kB。
现在标准库尝试以高效的方式使用内存。所以它必须找到一种方法将页面切成更小的部分并进行管理。为此,必须维护一些关于堆结构的额外信息。
这里很酷 Andrei Alexandrescu cppcon talk 或多或少是如何工作的(它省略了有关页面管理的信息)。
所以当你分配很多小对象时,关于堆结构的信息是相当大的。另一方面,如果您分配较少数量的较大对象会更有效 - 跟踪内存结构时占用的内存更少。
另请注意,有时根据堆策略(当请求小块内存时),浪费一些内存和 return 比请求的更大的内存更有效。
我一直在测试我自己的一些代码,看看需要多少分配的内存才能耗尽堆或空闲存储上的内存。但是,除非我的代码在测试时出错,否则在堆上可以放置多少内存方面,我会得到完全不同的结果。
我正在测试两个不同的程序。第一个程序在堆上创建向量对象。第二个程序在堆上创建整数对象。
这是我的代码:
#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
错误之前的最终输出是:"There are 2000 megabytes on the heap"
在第二个程序中:
#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
错误之前的最终输出是:"There are 511 megabytes on the heap"
两个程序的最终输出大不相同。我对免费商店有什么误解吗?我以为两个结果差不多。
很有可能pointers returned by new
on your platform are 16-byte aligned.
如果 int
是 4
字节,这意味着对于每个 new int(10)
,您将获得四个字节并使 12 个字节不可用。
仅此一项就可以解释从小分配中获得 500MB 可用 space 和从大分配中获得 2000MB 之间的区别。
除此之外,还有跟踪分配块的开销(至少,它们的大小以及它们是否空闲或正在使用)。这非常特定于您系统的内存分配器,但也会产生每次分配的开销。有关 glibc
分配器的解释,请参阅 https://sourceware.org/glibc/wiki/MallocInternals 中的 "What is a Chunk"。
首先,您必须了解操作系统分配内存以在称为页面的相当大的内存块中进行处理(它是硬件 属性)。页面大小约为 4 -16 kB。
现在标准库尝试以高效的方式使用内存。所以它必须找到一种方法将页面切成更小的部分并进行管理。为此,必须维护一些关于堆结构的额外信息。
这里很酷 Andrei Alexandrescu cppcon talk 或多或少是如何工作的(它省略了有关页面管理的信息)。
所以当你分配很多小对象时,关于堆结构的信息是相当大的。另一方面,如果您分配较少数量的较大对象会更有效 - 跟踪内存结构时占用的内存更少。
另请注意,有时根据堆策略(当请求小块内存时),浪费一些内存和 return 比请求的更大的内存更有效。