【问题标题】:how to catch out of memory exception in c++?如何在 C++ 中捕获内存不足异常?
【发布时间】:2011-12-06 15:17:24
【问题描述】:

谁能告诉我如何捕捉内存不足异常?

例如

try
{
    while(true)
    {
        int i = new int;
    }
}
catch( ? <--- what should be put here?)
{
    //exception handling
}

还有这个,

queue<int> q;
try
{
     while(true)
     {
          q.push(10);
     }
}
catch( ? <---- what should be put here?)
{
     //error handling
}

【问题讨论】:

标签: c++ exception try-catch


【解决方案1】:

抓住std::bad_alloc

您还需要一个处理错误的策略,因为您想做的许多事情都需要内存(即使只是在关闭之前向用户显示错误)。一种策略是在启动时分配一块内存,并在异常处理程序中delete它在尝试使用更多内存之前,以便有一些可用的内存。

【讨论】:

  • 请注意,在实际情况下,在抛出和捕获错误之间,堆栈展开(例如破坏本地 std::string 变量)可能已经释放了一些内存。通常,std::bad_alloc 的处理程序将相当“位于”程序的“外部”,而在深度嵌套的情况下内存可能会耗尽。只是说打印错误消息之类的事情不会必须失败。
【解决方案2】:

正如其他人所指出的,您要捕获的是std::bad_alloc。您也可以使用catch(...)catch(exception&amp; ex) 来捕获任何异常;后者允许在异常处理程序中读取和使用异常数据。

Mark Ransom 已经指出,当程序无法分配更多内存时,即使打印错误消息也可能会失败。考虑以下程序:

#include <iostream>

using namespace std;

int main() {
    unsigned long long i = 0;
    try {
        while(true) {
            // Leaks memory on each iteration as there is no matching delete
            int* a = new int;
            i++;
        }
    } catch(bad_alloc& ex) {
        cerr << sizeof(int) * i << " bytes: Out of memory!";
        cin.get();
        exit(1);
    }

    return 0; // Unreachable
}

(我强烈建议将程序编译为 32 位,以避免在 64 位机器上运行系统内存不足。32 位程序不能分配超过 4 GB 的内存,即 2 GB默认情况下在 Windows 上。)

当第一个bad_alloc 被抛出无限while 循环时,控制权被传递给catch 块,但程序仍然失败并出现未处理的异常。为什么?另一个bad_alloc 在尝试打印到cerr 时被抛出在异常处理程序中。您可以使用调试器来验证这一点:在catch(bad_alloc&amp; ex) 行设置断点,在调试器中运行程序,然后在到达断点后单步执行每个语句。在cerr 语句中将抛出bad_alloc 异常。

因此,要正确处理内存不足的情况,您需要留出一些内存,以便在退出之前打印错误消息。否则,程序将在尝试打印错误消息时因未处理的异常而崩溃。为此,您可以按照 Mark Ransom 的建议分配一块在异常处理程序中释放的内存:

// Reserve 16K of memory that can be deleted just in case we run out of memory
char* _emergencyMemory = new char[16384];
// ...
try {
// ...
} catch(bad_alloc& ex) {
    // Delete the reserved memory so we can print an error message before exiting
    delete[] _emergencyMemory;

    cerr << sizeof(int) * i << " bytes: Out of memory!";
    cin.get();
    exit(1);
}
//...

【讨论】:

  • 听说过 RAII 吗?如果你在做这样的软件工程,没有必要使用 C++——你可以使用 C。
  • 我见过有人声称,预分配内存的“技巧”导致永远不会遇到内存不足的情况。
  • 你为什么要把 std::bad_alloc 作为一个值?
  • 更新了代码以通过引用捕获异常。但是,此代码有意避免了 RAII,因为它专门用于出于演示目的而泄漏内存。这就是答案的重点。
【解决方案3】:
catch (std::bad_alloc& ba){
    cerr << "bad_alloc caught: " << ba.what() << endl;
}

作为注释,您应该阅读 bdonlan 的评论。对cerr 的调用很可能会失败。 Mark Ransom 在他的回答中的建议是缓解这个问题的好策略。

【讨论】:

  • 注意:由于内存不足,打印到 cerr 可能会失败。
  • @bdonlan:我同意你的看法。我没有考虑过这种情况,当我尝试分配非常大的向量时,我已经习惯了这个问题;)。我已经更新了我的答案。
  • @bdonlan:在我评论后非常缓慢:P
【解决方案4】:

你应该 catch 一个 std::bad_alloc 类型的对象。

或者,您也可以使用 newnothrow 版本:

int *pi = new (nothrow) int[N]; 
if(pi == NULL) 
{
   std::cout << "Could not allocate memory" << std::endl;
}

当您使用它时,如果new 失败,则不会引发异常。相反,它只是返回NULL,您在继续之前检查它。

【讨论】:

  • 我能否以某种方式将 nothrow 版本的 new 设置为默认值,这样我就不需要每次都写 (nothrow) 了?
  • @Quest:我不知道这是否可能。也许,作为最后的手段,你可以这样做:#define NEW new (nothrow) 然后int *pi = NEW int[N];
  • 我想我从来没有在我的代码中使用过这种形式的 new,成功分配内存不是可选的。
猜你喜欢
  • 2014-07-03
  • 2013-12-09
  • 2012-01-23
  • 2016-04-21
  • 2015-05-31
  • 2016-07-27
  • 2010-09-17
  • 1970-01-01
相关资源
最近更新 更多