无限扩展deque而不是向量时崩溃

时间:2014-10-02 15:20:05

标签: c++ vector deque

运行此代码时:

#include <iostream>
#include <vector>
#include <deque>

template< typename C  > 
void fillToMax( C & collection, typename C::value_type value )
{
     try
      {
         while( true )
           collection.push_back( value );

      }
    catch( std::bad_alloc const& )
      {
        std::cout << "bad alloc with size " << collection.size() << std::endl;
      }
      return;
}

void fillVector()
{
     std::vector<long> vecL;
     fillToMax( vecL, 123 );
}

void fillDeque()
{
      std::deque<long> deqL;
      fillToMax( deqL, 123 );
}

int main()
{
     fillVector();
     fillDeque();
}

我收到了预期的bad_alloc错误,因此很容易尝试/捕获。 问题是当我用vector替换deque时,在这种情况下我的机器崩溃了...黑屏,重新启动以及再次声称:你遇到了意想不到的问题!

我想使用deque代替vector来存储更多的项目而不会出现连续的空间问题。这将使我能够存储更多数据,但我无法承受我的应用程序崩溃,并想知道如何将其转换为bad_alloc。

这可能吗?

我的测试在win8.1上使用MinGW-W64 - gcc版本4.8.2(x86_64-posix-seh-rev4)

3 个答案:

答案 0 :(得分:4)

你不会说你正在使用什么系统,所以很难说,但是 一些系统&#34; overcommit&#34;,这基本上使符合 实现C ++(甚至C)是不可能的;系统会 说没有内存可用,并且崩溃 当你尝试使用它。 Linux是最广泛记录的 罪魁祸首在这里,但您可以重新配置它以正常工作。

你得到bad_alloc向量的原因是因为向量 分配更大的块。即使有过度使用,也是如此 如果块太大,系统将拒绝分配内存。 此外,许多malloc将使用不同的分配策略 非常大的块; IIRC,Linux中的malloc切换到使用 超出一定规模的mmap,系统可能会拒绝mmap 即使sbrk成功了。

答案 1 :(得分:1)

为什么矢量可能会崩溃而不是deque的快速答案是因为vector使用连续的缓冲区你将bad_alloc“更快”。而且还要求一大块。

为什么呢?因为您不太可能分配连续的缓冲区而不是较小的缓冲区。

vector将分配一定数量,然后为更大的缓冲区尝试一个大的“realloc”。有可能扩展当前的内存空间但可能没有,并且可能需要找到一大堆新的内存。

让我们说它看起来要扩大1.5倍。因此,您当前在使用的向量中有40%的可用内存,它需要找到60%的可用内存,但无法在当前位置执行此操作。好吧,它会带你到极限,所以它失败了bad_alloc,但实际上你只使用了40%的内存。

所以实际上有可用内存,那些使用“乐观”内存分配的操作系统不会意外地为你分配过多。你已经要求了很多而且它无法给你。 (他们并不总是非常乐观)。

另一方面,

deque一次要求一个块。你真的会耗尽你的记忆力,因此最好用于大型系列,但是它有一个缺点,当你内存不足时你确实用完了。而你可爱的乐观内存分配器无法处理它,你的进程就会死掉。 (它会杀死一些东西以增加记忆力。可悲的是它是你的。)


现在为您解决如何避免它发生的问题?您的答案可能是自定义分配器,即deque的第二个参数,它可以检查可用的实际系统内存,如果达到某个阈值则拒绝分配。

当然它取决于系统,但您可以为不同的机器使用不同的版本。

当然,您也可以设置自己的任意“限制”。

假设您的系统是Linux,您可以通过

关闭过度使用
'echo 2 > /proc/sys/vm/overcommit_memory'

您需要root(admin)权限才能执行此操作。 (或者让那些拥有它的人以这种方式配置它。)

否则,Linux手册中提供了检查内存使用情况的其他方法,通常在/proc中提及。

如果您的系统不是Linux而是另一个过度提交的系统,那么您必须通过编写自己的内存管理器来查找如何绕过它。否则,请采用任意可配置最大大小的简单选项。

请记住,对于deque,只有在需要分配新的“块”而不是每个push_back时才会调用分配器。

答案 2 :(得分:0)

Xarylem,只是试图回答&#34;我该如何防止这种情况发生?在这......

你知道抛出bad_alloc - std :: vector的东西。 你知道崩溃的东西...... std :: deque。

因此,一种方法是创建一个大小为X的新向量,如果成功,则清除向量并将X更多地推回到双端队列中。如果它没有,你知道你正陷入泥潭。类似的东西:

std::vector<int> testVector;
testeVector.reserve(1);
std::deque<int> actualDequeToFill;
for(size_t i = 0; ; ++i)
{
      //test first
      bool haveSpace = false;
      try { testVector.reserve(2); } catch(...) { haveSpace = false; }
      vector.reserve(1);
      if (!haveSpace) throw new std::bad_alloc("Vector shows no space left");
      deque.push_back(something);
}

这并非接近于万无一失......所以请将其用作解决方法的可能想法,而不是作为实现。

现在暂且不说了,我最好的猜测是......你的编译器不合规......正如我在评论中提到的那样,C ++ 需要 deque :: push_back来扔bad_alloc。如果可以,请远离该编译器(这是正确的基本内容)