【问题标题】:Understanding the amount of memory c++ is allocating了解 C++ 分配的内存量
【发布时间】:2017-08-21 13:54:08
【问题描述】:

我正在尝试更好地理解 C++ 中在堆上分配的内存量。我编写了一个小型测试程序,除了填充许多 2D 向量之外,它基本上什么也不做。我在 linux 64 位 VM 上运行它,并使用 valgrind 的 massif 工具来分析内存。

我正在运行此测试的环境:在 Win10 上的 VirtualBox 中运行的 Linux VM。 VM 配置:基本内存:5248MB,4 个 CPU,上限为 100%,磁盘类型 VDI(动态分配的存储)。

c++内存分析测试程序:

/**
 * g++ -std=c++11 test.cpp -o test.o
 */

#include <string>
#include <vector>
#include <iostream>

using namespace std;

int main(int argc, char **arg) {
    int n = stoi(arg[1]);
    vector<vector<int> > matrix1(n);
    vector<vector<int> > matrix2(n);
    vector<vector<int> > matrix3(n);
    vector<vector<int> > matrix4(n);
    vector<vector<int> > matrix5(n);
    vector<vector<int> > matrix6(n);
    vector<vector<int> > matrix7(n);
    vector<vector<int> > matrix8(n);

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix1[i].push_back(j);
        }
    }

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix2[i].push_back(j);
        }
    }

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix3[i].push_back(j);
        }
    }

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix4[i].push_back(j);
        }
    }

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix5[i].push_back(j);
        }
    }

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix6[i].push_back(j);
        }
    }

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix7[i].push_back(j);
        }
    }

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix8[i].push_back(j);
        }
    }
}

我运行以下 bash 脚本以提取 n 不同值的内存配置文件(test.o 是上面的程序,使用 g++ -std=c++11 编译,g++ 是版本 5.3.0)

valgrind --tool=massif --massif-out-file=massif-n1000.txt ./test.o 250
valgrind --tool=massif --massif-out-file=massif-n1000.txt ./test.o 500
valgrind --tool=massif --massif-out-file=massif-n1000.txt ./test.o 1000
valgrind --tool=massif --massif-out-file=massif-n2000.txt ./test.o 2000
valgrind --tool=massif --massif-out-file=massif-n4000.txt ./test.o 4000
valgrind --tool=massif --massif-out-file=massif-n8000.txt ./test.o 8000
valgrind --tool=massif --massif-out-file=massif-n16000.txt ./test.o 16000
valgrind --tool=massif --massif-out-file=massif-n32000.txt ./test.o 32000

这给了我以下结果:

|--------------------------------|
| n     | peak heap memory usage |
|-------|------------------------|
| 250   | 2.1 MiB                |         
| 500   | 7.9 MiB                |
| 1000  | 31.2 MiB               |
| 2000  | 124.8 MiB              |
| 4000  | 496.5 MiB              |
| 8000  | 1.9  GiB               |
| 16000 | 6.2 GiB                |
| 32000 | 6.1 GiB                |
|--------------------------------|

每个矩阵的大小为 n^2,我总共有 8 个矩阵,因此我预计内存使用量约为 f(n) = 8 * n^2

问题1从n=250到n=8000,为什么n*=2时内存使用量或多或少乘以4?

从 n=16000 到 n=32000 发生了一些非常奇怪的事情,因为 valgrind 实际上报告了内存减少。

问题2在n=16000和n=32000之间发生了什么,怎么可能堆内存少了,而理论上应该分配更多的数据?

请参见下面 n=16000 和 n=32000 的 massif-visualizer 输出。

【问题讨论】:

  • 我建议使用您正在运行测试的编译器和平台标记这个问题。内存分配的内部机制由你的实现决定,c++ 对这个主题几乎没有强加于人。
  • 我的理解是vector如何管理其内存在一定程度上取决于编译器。通常vector 的内存管理包括某种程度的内存预分配以及某种程度的内存分配预测。看看frogatto.com/2009/11/17/how-cs-vector-works-the-gritty-details
  • 不使用 push_back 实际上会改变您正在寻找的统计信息,主要是因为当数组空间不足时需要不断地重新分配空间来复制数组?我在想衡量实际分配的更好方法是使用 .resize 或 .reserve ?这样,您就不会支付副本的开销。请随意粉碎我的理解......
  • 测试分析程序反映了一个使用 push_back 的真实程序,如果 push_back 改变了统计信息,那么它应该反映在分析结果中,这是一件好事,以便了解我的内存是如何分配的真正的节目。但是是的,我相信你是对的,如果在现实世界的程序中你可以使用 .reserve 或 .resize,你可能应该这样做以减少重新分配开销,有关此问题的详细文章可在此处获得:frogatto.com/2009/11/17/how-cs-vector-works-the-gritty-details

标签: c++ memory memory-management valgrind


【解决方案1】:

1) 因为矩阵向量的大小(以及它们的内存占用)随着 n2 增长,所以将 n 加倍会导致内存使用量翻两番。任何与精确关系的偏差(相对于渐近)都是由于不同的因素造成的(例如malloc / std::allocator使用的元数据,vector使用的块大小加倍方法)

2) 你的内存开始用完了,所以 Linux 开始 page 一些;如果您想查看 total(活动 + 分页)内存使用情况,请使用 --pages-as-heap=yes。 (来源:http://valgrind.org/docs/manual/ms-manual.html

【讨论】:

  • 我尝试使用选项 --pages-as-heap=yes 在 n=16000 运行我的测试,不幸的是这会导致 valgrind 崩溃。它从大量“线程#1 中的 brk 段溢出,不能增长到 0x4a2c000”错误开始,直到 valgrind 最终耗尽内存并崩溃。
  • @Gio 也许this post 会帮助你
  • 最新的 Valgrind 3.13 也稍微放宽了内存限制。来自valgrind.org/docs/manual/dist.news.htmlThe amount of memory that Valgrind can use has been increased from 64GB to 128GB. In particular this means your application can allocate up to about 60GB when running on Memcheck.
猜你喜欢
  • 2019-07-08
  • 2011-05-30
  • 1970-01-01
  • 1970-01-01
  • 2021-06-18
  • 1970-01-01
  • 1970-01-01
  • 2013-05-26
  • 1970-01-01
相关资源
最近更新 更多