C ++ new分配的空间比预期的多

时间:2016-01-07 18:17:23

标签: c++ memory-management malloc new-operator

我试图在内存要求很高时测试一些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;    
}

它应该分配大约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 ++中的每个新节点至少分配32个字节是真的吗?

3 个答案:

答案 0 :(得分:4)

简短回答 - 你忘了考虑内存分配开销。内存分配器本身需要跟踪已分配的内存块,这些内存本身会占用内存,如果您分配了大量小块,则与您请求的内存量相比,开销会变得过大。然后还要考虑块对齐,许多分配器尝试智能并对齐内存块以获得最佳CPU访问速度,因此它们将与高速缓存行对齐。

最后但并非最不重要的是,一个成功的请求给你8个字节的内存可能会在幕后分配更大的块。毕竟,向malloc / new询问特定数量的内存只能保证您获得至少大小的大小,而不是那个大小。

对于分配大量小块的用例,您需要查看类似于池分配器的内容,以最大限度地减少开销。

实际上,您应该考虑的是比具有大量小节点的非常大的链表更好的数据结构。

答案 1 :(得分:0)

如果您只想了解正在使用的分配,则它具有8字节开销,最小32字节,包括开销和16字节对齐。例如:

1 .. 24字节:需要32
25 .. 40字节:需要48
41 .. 56字节:需要64
等。

如果你想有效地使用大量微小物体,你需要以其他方式分配它们(批量处理,然后自己细分分配)。

答案 2 :(得分:0)

这实际上取决于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
相关问题