当内存需求很高时,我正在尝试测试某些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; }
仅仅因为Node类占用8个字节,就应该分配大约1 GB的数据。我已经通过sizeof,gdb甚至valgrind对此进行了验证。
但是,该程序分配了大约4 GB的数据!如果我将此大小增加一倍((120000000-> 2400000000),则有2个选项(我的笔记本电脑安装了8GB RAM):
如果我已关闭交换区域,则该进程将被内核杀死。
如果没有,则发生分页,并且操作系统变得非常慢。
关键是我无法测试分配2 GB数据的应用程序,因为它消耗8 GB的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导致使用4 GB RAM,240000000导致我的应用被Linux内核杀死。
所以我碰到了这篇文章
C ++中的每个新函数是否至少分配32个字节,这是真的吗?
简短的答案-您忘记了内存分配开销。内存分配器本身需要跟踪已分配的内存块,这些内存块本身会消耗内存,如果您分配了很多小块,则与请求的内存量相比,开销会变得不合理。再有就是要考虑的块对齐方式,很多分配器都试图变得聪明并对齐内存块以实现最佳的CPU访问速度,因此它们将与高速缓存行对齐。
最后但并非最不重要的一点是,成功地请求您提供8个字节的内存可能已经在后台分配了更大的块。毕竟,要求malloc / new提供特定数量的内存只能保证您将获得至少具有该大小的块,而不是恰好具有该大小的块。
对于分配大量小块的用例,您需要研究类似于池分配器的方法,以最大程度地减少开销。
实际上,您可能应该考虑的是一种比带有许多小节点的超大型链表更好的数据结构。