为什么这个程序保留并保留2 GB内存而不是1 GB?

时间:2013-06-09 09:54:23

标签: c++ memory vector

#include <vector>
typedef std::vector<char> vc;
typedef std::vector<vc> vvc;

vvc f() {
  const int N = (1 << 15);
  return vvc(N, vc(N)); // 1 GB
}

int main () {
  vvc v; 
  v = f();
  while(true);  // Why 2GB allocated ?
  return 0;
}

用clang -O2和g ++ -O2编译。同样的行为。

编辑:在一些答案中有多种方法可以修复此代码。 但我的观点是要理解这段代码。当然有一个临时对象,但它应该在分号处消失,并且应该将1GB的内存返回给系统。这个问题打算问为什么它不会发生。

编辑2:临时对象的析构函数确实在分号之前调用。

5 个答案:

答案 0 :(得分:4)

您的代码中的作业我们有充分的理由徒劳地宣传; - )

如果您使用了初始化,那么允许进行RVO优化,并且正如其他实验一样,它实际上似乎也在起作用。

使用赋值函数返回一个对象,然后必须将其复制到目标,然后才能对临时文件进行核对。虽然数据流肛门可能会被优化,但这是一个难以捕捉并被认为难得使用的案例。因此,您将在所有其他方面受到性能损失。

编辑:要看到临时确实在适当的时候点,我建议使用调试器和单步执行功能。或者看看assy上市。对于抽象机器,dtor调用必须在下一条指令之前发生。

然而,优化的代码更自由地使用'as''规则。如果代码无法区分它,它可能会推迟一点,你可能会体验到这种效果。

C ++ 11注意:在C ++ 11中,向量获得了另一个从rvalue移动的op =。这样可以解决这个特殊情况,但是需要时间才能让所有编译器缩小差距,特别是所有类都能获得移动的东西。寻找移动的前11代码将使用swap()代替=。

答案 1 :(得分:4)

我的猜测是,您正在查看操作系统显示分配给进程的内存量,以及您的编译器还不支持C ++ 11的移动分配。< / p>

因此会发生以下情况:

  • 在您的函数中,您分配向量(消耗1GB内存)。
  • 然后,在你的作业中,复制了该向量(消耗了1GB的内存,相邻到第一个块)。
  • 之后,原始矢量被破坏。为程序中的未来分配释放内存,但由于该内存之前保留向量的内存,并且对于大多数操作系统,分配给进程的内存必须是连续的,进程可以< em> not 将其还原回操作系统。

因此,结果是操作系统为您的进程分配2GB,其中1GB分配给向量,1GB可用于将来在您的程序中分配(但不能用于另一个进程中的分配)。

答案 2 :(得分:2)

我刚刚在win32,vc7上测试过。

您的代码分配2Gb。如果改为:

int main () {
  vvc v = f();
  while(true);

它只需要1Gb。

我想原因 - 是矢量之间的复制操作。

v = f() - 调用构造函数。您的情况 - 默认c&tor;并复制(运算符=)到空对象。应对需要2 Gb。

f()(创建向量和返回)的内部操作可以使用RVO,并且没有任何应对和额外分配。

答案 3 :(得分:1)

  

编辑:有多种方法可以修复这些代码中指向的代码   答案。但我的观点是要理解这段代码。当然有一个   临时对象,但它应该在分号和1GB的时候消失   内存应该返回给系统。这个问题打算问   为什么没有发生。

在Windows上,我用gcc编译你的测试并运行。我发现Private Bytes的值在程序启动后会增加,然后开始减少然后最终停止变化。所以在这个操作系统内存返回系统。顺便说一下,我使用Process Explorer来获取有关Private Bytes的信息。

我假设您在Linux上进行了测试,并且此操作系统内存未返回给系统。如果它没有被使用,如果我是对的话,它最终被移动到寻呼区域。

答案 4 :(得分:0)

我认为会复制返回值。比暂时的2个vvc副本存在。 每个都有2 ^ 30个字符(= 1GB)

据我所知,允许编译器但不需要删除复制操作

我找到了这个链接 http://en.wikipedia.org/wiki/Return_value_optimization