C++"new"运算符能否在现实生活中抛出异常?

osg*_*sgx 44 c++ exception out-of-memory new-operator

new操作员可以在现实生活中抛出异常吗?

如果是这样,除了杀死我的应用程序之外,我是否有任何处理此类异常的选项?

更新:

任何真实的,重要的new应用程序检查失败并在没有内存时恢复吗?


也可以看看:

Jam*_*lis 41

是的,new如果分配失败,可以并且将会抛出.如果内存不足或者尝试分配的内存块太大,就会发生这种情况.

您可以捕获std::bad_alloc异常并适当地处理它.有时这是有道理的,有时候(读:大多数时候)它没有.例如,如果您尝试分配一个巨大的缓冲区但可以使用较少的空间,则可以尝试连续分配较小的块.

  • 你应该在任何可以合理恢复的地方捕获`std :: bad_alloc`.在大多数情况下,你可以做的事情并不多,所以你最好的选择就是在'main`中捕获它,至少给用户一个友好的错误信息或记录失败(许多专家,包括Herb Sutter,同意这一点:http://www.gotw.ca/publications/mill16.htm). (10认同)
  • 当你写C时,你是否检查malloc是否返回NULL?如果没有,我怀疑我能说服你注意新的例外情况. (8认同)
  • 我的天啊!你不检查fclose()的返回值?! (6认同)
  • 我已经做了一些嵌入式系统开发,我可以肯定地说,如果你不检查new/malloc操作是否成功,某个用户某处会找到一种方法来填充设备的内存,如果代码崩溃你的应用程序缺乏适当的检查.不检查功能的返回是BAD BAD练习. (3认同)
  • @osgx:在GNU程序中,惯例是调用它(`malloc`成功或死掉)函数`xmalloc`. (2认同)

Bri*_*ndy 24

新的运算符和new []运算符应该抛出std::bad_alloc,但情况并非总是如此,因为行为有时可以被覆盖.

人们可以使用std::set_new_handler,突然发生一些完全不同的事情,而不是扔std::bad_alloc.虽然标准要求用户使内存可用,中止或抛出std::bad_alloc.但当然情况可能并非如此.

免责声明:我不打算这样做.

  • 这就是我所谓的不良做法. (7认同)
  • `<new>`中的`std :: set_new_handler`是标准C++,§18.4.2.2-3.例如,如果您有某种垃圾收集,或者您想记录错误,那么使用它是完全合理的.通过`throw bad_alloc`退出new_handler并不是一个坏主意. (7认同)
  • 另外 - 标准要求用户的新处理程序使内存可用,中止或抛出bad_alloc. (3认同)

Per*_*uid 19

如果您运行的是运行Linux且没有虚拟内存的典型嵌入式处理器,那么如果您分配了太多内存,则新的操作系统很可能会终止您的进程.

如果您在物理内存小于最大虚拟内存(标准Windows上为2 GB)的计算机上运行程序,您会发现一旦分配了大约等于可用物理内存的内存量,进一步分配将成功但会导致分页到磁盘.这会使您的程序陷入困境,实际上您可能无法达到耗尽虚拟内存的程度.所以你可能不会抛出异常.

如果您拥有比虚拟内存更多的物理内存,并且只是继续分配内存,那么当您将虚拟内存耗尽到无法分配所请求的块大小时,您将收到异常.

如果你有一个长期运行的程序,它分配和释放许多不同的块大小,包括具有各种生命周期的小块,虚拟内存可能会碎片化到新的无法找到足够大的块的程度.满足要求.然后new将抛出异常.如果你碰巧有一个内存泄漏漏掉了随机位置的偶然小块,最终会将内存碎片到任意小块分配失败的程度,并抛出异常.

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

以上所有内容均为默认的全局新内容.但是,您可以替换global new,您可以提供特定于类的新功能.这些也可以抛出,这种情况的意义取决于你如何编程.新的通常包括一个循环,尝试所有可能的途径来获取所请求的内存.当所有人都筋疲力尽时,它会抛出.你做什么取决于你.

您可以从new中捕获异常并使用它提供的机会来记录异常时的程序状态.你可以"转储核心".如果在程序启动时分配了循环检测缓冲区,则可以在终止程序之前将其转储到磁盘.程序终止可以是优雅的,这比仅仅不处理异常更有优势.

我没有亲自看过一个例子,在异常之后可以获得额外的内存.然而,一种可能性如下.假设您有一个高效但不擅长回收可用空间的内存分配器.例如,它可能容易出现自由空间碎片,其中自由块相邻但未合并.您可以使用new中的异常(在new_handler中捕获)在重试之前运行可用空间的压缩过程.

严肃的程序应该将内存视为潜在的稀缺资源,尽可能地控制其分配,监控其可用性并在事情看起来严重错误时做出适当的反应.例如,您可以假设在任何实际程序中传递给内存分配器的size参数都有相当小的上限,而大于此值的任何内容都会导致某种错误处理,无论请求是否可以满意.您可能会争辩说应该监视长期运行程序的内存增加率,如果可以合理地预测该程序将在不久的将来耗尽可用内存,则应该开始有序重启该过程.


Chr*_*ung 9

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


OP编辑的更新:从内存不足情况恢复的程序的最典型情况是垃圾收集系统,然后执行GC并继续.虽然,这种按需GC实际上只适用于最后的努力; 通常,好的程序会定期尝试GC以减少对收集器的压力.

非GC程序从内存不足的问题中恢复的情况不常见,但对于面向Internet的服务器,恢复的一种方法是简单地拒绝导致内存耗尽"临时"错误的请求.("先进先出"策略.)

  • 还有GUI应用程序.如果用户操作导致内存耗尽.然后放弃当前的操作,而不是整个应用程序. (6认同)

vla*_*adr 7

这取决于编译器/运行时以及operator new您正在使用的内容(例如,某些版本的Visual Studio 不会抛出开箱即可,而是宁愿返回一个NULL指针malloc.)

您可以始终catch使用std::bad_alloc异常,或明确使用nothrow new返回NULL而不是抛出.(另请参阅过去围绕主题的StackOverflow帖子.)

需要注意的是operator new,像malloc,当你耗尽内存,(在根据操作系统的32位进程如2-3GB)失败了的地址空间,配额外的(ulimit已经提到)或出连续的地址空间(例如碎片堆.)


moo*_*ogs 7

您不需要在每个单独处理异常new:)异常可以传播.设计您的代码,以便在每个"模块"中存在处理该错误的特定点.


Leo*_*Hat 7

osgx说:

是否有任何真实应用程序检查大量新闻并在没有内存时可以恢复?

我之前在回答这个问题时回答这个问题,引用如下:

处理这种情况非常困难.您可能希望向应用程序的用户返回有意义的错误,但如果由于内存不足而导致问题,您甚至无法负担分配错误消息的内存.这真的是一个抓住22的情况.

有一种防御性编程技术(有时称为内存降落伞或雨天基金),您可以在应用程序启动时分配一块内存.然后,当您处理bad_alloc异常时,释放此内存,并使用可用内存正常关闭应用程序,包括向用户显示有意义的错误.这比崩溃要好得多:)