C ++“new”运算符能否在现实生活中抛出exception?

new运营商能否在现实生活中抛出exception?

如果是的话,除了杀死我的应用程序之外,还有什么办法可以处理这种exception吗?

更新:

有没有真正的世界, new应用程序检查失败,并恢复时,没有内存?


也可以看看:

  • 你多久检查一次C ++新指令中的exception?
  • 在C ++中testing“新”的返回是否有用?
  • 在任何情况下,新的返回NULL?

new运算符和new []运算符应该抛出std::bad_alloc ,但这并不总是这种情况,因为行为有时可能被覆盖。

可以使用std::set_new_handler ,突然之间可能会发生完全不同的事情比扔std::bad_alloc 。 虽然标准要求用户要么使内存可用,放弃,要么抛出std::bad_alloc 。 但当然情况可能并非如此。

免责声明:我不是build议这样做。

是的,如果分配失败, new可能会抛出。 如果内存不足或尝试分配的内存块太大,就可能发生这种情况。

您可以捕获std::bad_allocexception并进行适当的处​​理。 有时候这是有道理的,其他时候(大部分时候都是这样)没有。 例如,如果您正在尝试分配一个巨大的缓冲区,但是可以使用较less的空间,则可以尝试连续分配较小的块。

如果你正在一个运行Linux的典型embedded式处理器上运行而没有虚拟内存,那么如果你分配了太多的内存,在新的失败之前,操作系统很可能会终止你的进程。

如果您在物理内存less于最大虚拟内存(标准Windows上为2 GB)的计算机上运行程序,则会发现一旦分配了大致等于可用物理内存的内存量,进一步的分配将会成功但会导致分页到磁盘。 这会让你的程序停滞不前,你实际上可能无法完成耗尽虚拟内存的工作。 所以你可能不会得到一个exception抛出。

如果你的物理内存比虚拟内存多,而且你只是简单地分配内存,那么当虚拟内存耗尽时,你将会得到一个exception,直到你不能分配你请求的块大小。

如果你有一个长期运行的程序,分配和释放许多不同的块大小,包括具有各种各样的生命周期的小块,虚拟内存可能会变得碎片化,以至于新的块将无法find足够大的块满足要求。 那么新会抛出一个exception。 如果你碰巧有一个内存泄漏,在偶然的位置偶尔会泄漏一个小块,最终会将内存碎片化到任意小块分配失败的地步,并且会抛出一个exception。

如果你有一个程序错误,意外地将一个巨大的数组大小传递给new [],new将会失败并抛出一个exception。 例如,如果数组大小实际上是某种随机字节模式,可能是由未初始化的内存或已损坏的通信stream导出的,则会发生这种情况。

以上全部为默认全局新增。 但是,您可以replace全球新的,你可以提供特定于类的新的。 这些也可以抛出,这种情况的意义取决于你如何编程。 通常包括一个循环,尝试所有可能的途径来获得请求的内存。 当所有这些都用尽时,它会抛出。 你做什么取决于你。

您可以捕捉到新的exception,并利用它提供的机会来loggingexception发生时的程序状态。 你可以“转储核心”。 如果在程序启动时分配了循环的检测缓冲区,则可以在终止程序之前将其转储到磁盘。 程序终止可以是优雅的,这比简单地不处理exception更有优势。

我没有亲眼看到一个例子,在exception之后可以获得额外的内存。 一种可能性是以下。 假设你有一个高效的内存分配器,但是不善于回收可用空间。 例如,它可能容易出现空闲空间碎片,其中空闲块相邻但不结合。 您可以使用new中的exception,在new_handler中捕获,在重试之前运行可用空间的压缩过程。

严肃的scheme应该把记忆当作一种潜在的稀缺资源,尽可能地控制它的分配,监测它的可用性,并在出现明显的错误时作出适当的反应。 例如,你可以在任何一个真正的程序中都有一个例子,在传递给内存分配器的size参数上有一个很小的上限,而大于这个值的任何东西都会导致某种error handling,无论请求是否可以满意。 你可能会争辩说,一个长期运行的程序的内存增加速度应该被监视,如果可以合理地预测到程序将在不久的将来用尽内存,那么应该开始有序地重新启动程序。

在Unix系统中,习惯于运行带有内存限制的长时间运行的进程(使用ulimit ),以便它不会占用系统的所有内存。 如果你的程序达到了这个限制,你会得到std::bad_alloc


更新OP的编辑:程序从内存不足状态中恢复的最典型情况是垃圾收集系统,然后执行GC并继续。 虽然这种点播式的GC只是最后的努力, 通常情况下,好的程序会定期尝试GC以减less收集器的压力。

非GC程序从内存不足问题恢复的情况不太常见,但对于面向Internet的服务器,恢复的一种方法是仅仅拒绝导致内存用完“临时”错误的请求。 (“先来先服务”战略)

你不需要处理每一个newexception:)例外可以传播。 devise你的代码,以便在每个“模块”中有处理错误的地方。

它依赖于编译器/运行时和你正在使用的operator new (例如某些版本的Visual Studio 不会抛出包装盒 ,而是宁愿返回一个指向la mallocNULL指针)。

你总是可以catch一个std::bad_allocexception,或者显式地使用nothrow new来返回NULL而不是抛出。 (另请参阅围绕主题的过去的StackOverflow文章 。)

请注意,像malloc这样的operator new 在内存不足,地址空间不足(例如32位进程中的2-3GB(取决于操作系统)),超出配额(已提及ulimit )或出连续的地址空间(例如碎片堆)。

osgx说:

任何真实世界的应用程序是否会检查大量的新闻,并在没有内存的情况下可以恢复?

我在回答 这个问题 时曾回答过这个问题 ,

处理这种情况是非常困难的。 您可能希望向应用程序的用户返回一个有意义的错误,但是如果这是由于内存不足造成的问题,则可能无法负担内存来分配错误消息。 这实际上有点捉襟见肘。

有一种防御性编程技术(有时称为记忆降落伞或雨天基金),当您的应用程序启动时,您可以在其中分配一大块内存。 当您处理bad_allocexception时,您可以释放这些内存,并使用可用的内存来优雅地closures应用程序,包括向用户显示一个有意义的错误。 这比崩溃好多了:)

是的, new可以抛出std::bad_allocstd::exception的子类),你可能会捕获。

如果你绝对想避免这个exception,而是准备testing一个空指针的new结果,你可以添加一个nothrow参数:

 T* p = new (nothrow) T(...); if (p == 0) { // Do something about the bad allocation! } else { // Here you may use p. } 

如果没有更多的内存可用, new会抛出一个exception,但是这并不意味着你应该把每一个新的东西包装在try ... catch 。 只有在程序实际上能做些什么的情况下才能发现exception。

如果程序不能做任何事情来处理这种exception情况,如果内存不足,通常情况下,捕捉exception是没有用的。 如果你能合理地做的唯一事情就是放弃程序,那么就让exception冒泡到最高层,在那里也会终止程序。

在许多情况下,对于内存不足情况没有合理的恢复,在这种情况下,让应用程序终止可能是完全合理的。 您可能想要捕捉exception高级别以显示比编译器可能默认给出更好的错误信息,但您可能需要发挥一些技巧才能使其工作(因为该过程可能是非常低的在这一点上的资源)。

除非你有一个可以处理和恢复的特殊情况,否则可能没有理由花费大量精力来处理这个exception。

请注意,在Windows中,非常大的new / malloc只会从虚拟内存中分配。 在实践中,你的机器会在你看到exception之前崩溃。

 char *pCrashMyMachine = new char[TWO_GIGABYTES]; 

如果你敢的话试试吧!

我使用的是Mac OS X,而我从来没有见过malloc返回NULL (这意味着从C ++中的new例外)。 机器陷入困境,尽最大努力为进程分配不断减less的内存,最后发送SIGSTOP并邀请用户终止进程,而不是处理分配失败。

但是,这只是一个平台。 一定有默认分配器抛出的平台。 而且,正如克里斯所说, ulimit可能会引入一个人为的约束,这样一个例外就是预期的行为。

此外,除了默认的一个/ malloc还有分配器。 如果一个类重写operator new ,你使用new(…)自定义参数,或者将一个allocator对象传入一个容器,它可能定义了自己的条件来抛出bad_alloc

如果池中没有足够的可用内存来满足运行时请求,则新运算符将抛出std :: bad_allocexception。

这可能发生在糟糕的devise上,或者当分配的内存没有被正确释放。

处理这种exception是基于你的devise,一种方法是暂停,稍后重试,希望更多的内存返回到池中,请求可能会成功。

由于限制资源的决定,大多数现实的新情况将会出现。 说这个类(这可能是内存密集型的)将物理池中的内存从物理池中取出,并且如果有许多对象从内存中取出(我们需要内存来存储声音,纹理等其他内容),那么它可能会抛出,而不是稍后崩溃,能够分配内存需要它。 (看起来像一个奇怪的副作用)。

对于内存受限的设备来说,重载新内容可能很有用。 比如掌上电脑或者在游戏机上的时候,太容易过冷的效果。

是的,新的可以扔掉。

既然你在问“真正的”程序:我已经从事了各种缩小包装的商业软件应用程序超过20年了。 拥有数百万用户的“真正的”节目。 你今天可以去买货架。 是的,新的可以抛出。

有各种方式来处理这个。

首先,编写你自己的new_handler(在新的放弃和抛出之前调用 – 参见set_new_handler()函数)。 当你的new_handler被调用时,看看你是否可以释放一些你并不需要的东西。 还警告用户他们内存不足。 (是的,如果你真的很低,可能很难警告用户。

一件事就是在程序开始时预先分配一些额外的内存。 当内存不足时,请使用此额外的内存来帮助将用户文档的副本保存到磁盘。 然后警告,也许优雅地退出。

等等,这只是一个概述,显然还有更多。

处理低内存并不容易。

新的处理程序函数是分配函数在分配内存失败时所调用的函数。 我们可以有自己的日志logging或者一些特殊的动作,例如,安排更多的内存等。它的目的是三件事之一:1)提供更多的内存2)终止程序(例如通过调用std :: terminate)3 )抛出std :: bad_alloctypes的exception或从std :: bad_alloc派生的exception。 默认实现抛出std :: bad_alloc。 用户可以拥有自己的新处理程序,这可能会提供不同于默认处理程序的行为。 这只能在你真正需要的时候使用。 请参阅示例以获取更多的说明和默认行为,

 #include <iostream> #include <new> void handler() { std::cout << "Memory allocation failed, terminating\n"; std::set_new_handler(nullptr); } int main() { std::set_new_handler(handler); try { while (true) { new int[100000000ul]; } } catch (const std::bad_alloc& e) { std::cout << e.what() << '\n'; } } 

当你从外部(例如用户空间,networking)分配内存时检查/捕获这个exception是很好的,因为这可能意味着企图破坏你的应用程序/服务/系统,你不应该允许这个发生。

new运算符会抛出内存(精确的虚拟内存)时抛出std::bad_allocexception。

如果new引发exception,那么这是一个严重的错误:

  • 超过可用虚拟机正在分配(最终失败)。 通过捕获std::bad_allocexception,您可以尝试减less内存数量。