当内存需求很高时,我试图测试一些c++应用程序的行为,但似乎无法使用所有可用的ram。我有以下程序:
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;
}
它应该分配大约1GB的数据,因为Node类占用了8个字节。我已经通过sizeof,gdb,甚至valgrind验证了这一点。
然而,这个程序分配了大约4 GB的数据!如果我将这个大小增加一倍(120000000->2400000000),那么有两个选项(我的笔记本电脑安装了8GB的RAM):
- 如果我关闭了交换区域,进程就会被内核杀死
- 如果没有,那么分页就会发生,操作系统就会变得非常慢
重点是,我无法测试一个分配2GB数据的应用程序,因为它消耗8GB的RAM!
我想,当我请求一个新的Node时,分配的字节可能超过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导致使用了4GB的RAM,240000000导致我的应用程序被Linux内核杀死。
所以我偶然发现了这个后
C++中的每一个new都至少分配32个字节,这是真的吗?
简短的回答-您忘记考虑内存分配开销。内存分配器本身需要跟踪分配的内存块,这些内存块本身会消耗内存,如果你分配了很多小块,那么与你请求的内存量相比,开销会变得不合理地大。然后还有块对齐需要考虑,很多分配器都试图变得智能,并对齐内存块以获得最佳的CPU访问速度,因此它们将与缓存线对齐。
最后但同样重要的是,一个给你8字节内存的成功请求很可能在幕后分配了一个更大的块。毕竟,向malloc/new请求特定数量的内存只能保证你得到的块至少是那个大小,而不是那个大小。
对于分配大量小块的用例,您需要研究类似于池分配器的东西,以最大限度地减少开销。
实际上,您可能应该考虑的是一个更好的数据结构,而不是一个有很多小节点的大型链表。
如果你只想了解你正在使用的分配,它有8字节的开销,最小32字节,包括开销和16字节的对齐。例如:
1。。24字节:占用32
25。。40字节:占用48
41。。56字节:占用64
等
如果你想有效地使用大量的小对象,你需要以其他方式分配它们(批量分配,然后自己细分分配)。
它实际上取决于malloc
的实现。我在我的机器(64位)上测试过,如果我使用tcmalloc
,大约需要1GB的内存。tcmalloc在内部为不同的分配大小保留单独的池,因此对于每个池,不需要对对象大小进行记账,这减少了小对象的开销。对于8字节的分配,根本没有开销。
hidden $ cat c.cpp
#include <iostream>
#include <string>
using namespace std;
struct Node { Node *next; };
int main() {
Node *first = new Node();
Node *last = first;
for (int i=0; i < 120000000; i++) {
Node *node = new Node();
node->next = 0;
last = last->next = node;
}
cout << "Press <Enter> to continue...";
string s;
cin >> s;
return 0;
}
hidden $ g++ -std=c++11 -O3 c.cpp /usr/lib/libtcmalloc_minimal.so.4
hidden $ ./a.out & { sleep 5; ps -C a.out -o rss; killall a.out; }
[1] 31500
Press <Enter> to continue...
[1]+ Stopped ./a.out
RSS
947064