当内存要求很高时,我正在尝试测试一些 C++ 应用程序的行为,但似乎我无法使用所有可用的内存。我有以下程序:
class Node {
public:
Node *next;
};
int main() {
int i=0;
Node *first = new Node();
Node *last = first;
//Should be 120000000 * 8 bytes each -> approx 1 GB
for (i=0; i < 120000000; i++) {
Node *node = new Node();
node->next = 0;
last->next = node;
last = last->next;
}
for (i=0; i < 120000000; i++) {
Node *oldfirst = first;
first = first->next;
delete oldfirst;
}
delete first;
return 0;
}
应该分配大约 1 GB 的数据,因为 Node 类占用 8 个字节。我已经通过 sizeof、gdb,甚至 valgrind 验证了这一点。
但是这个程序分配了大约 4 GB 的数据!如果我将这个大小加倍( 120000000 -> 2400000000 ),那么有 2 个选项(我的笔记本电脑安装了 8GB RAM):
- 如果我关闭了交换区,进程就会被内核杀死。
- 如果没有,则会进行分页,操作系统会变得非常慢。
关键是我无法测试分配 2 GB 数据的应用程序,因为它消耗 8 GB 的 RAM!
我想也许我请求一个新节点时分配的字节数超过了 8(即 Node 对象的大小),所以我尝试了以下操作:
class Node {
public:
Node *next;
Node *second_next;
};
int main() {
int i=0;
Node *first = new Node();
Node *last = first;
//Should be 120000000 * 8 bytes each -> approx 1 GB
for (i=0; i < 120000000; i++) {
Node *node = new Node();
node->next = 0;
last->next = node;
last = last->next;
}
for (i=0; i < 120000000; i++) {
Node *oldfirst = first;
first = first->next;
delete oldfirst;
}
delete first;
return 0;
}
现在Node对象占用16个字节。应用程序的内存占用完全一样! 120000000 导致使用了 4 GB RAM,240000000 导致我的应用程序被 Linux 内核杀死。
所以我遇到了this post
C++ 中的每个 new 都至少分配 32 个字节是真的吗?
最佳答案
简短的回答 - 你忘了考虑内存分配开销。内存分配器本身需要跟踪分配的内存块,这些内存块本身会消耗内存,如果您分配很多小块,与您请求的内存量相比,开销会大得不合理。然后还要考虑 block 对齐,许多分配器都试图变得聪明并对齐内存块以获得最佳 CPU 访问速度,因此它们将与缓存行对齐。
最后但并非最不重要的一点是,给你 8 字节内存的成功请求很可能在幕后分配了更大的 block 。毕竟,向 malloc/new 请求特定数量的内存只能保证您将获得至少那个大小的 block ,而不是那个大小。
对于分配很多很多小块的用例,您需要研究像池分配器这样的东西,它可以最大限度地减少开销。
实际上,您应该考虑的是一种比具有许多小节点的非常大的链表更好的数据结构。
关于C++ new 分配比预期更多的空间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34662323/