【发布时间】: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