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_alloc
exception并进行适当的处理。 有时候这是有道理的,其他时候(大部分时候都是这样)没有。 例如,如果您正在尝试分配一个巨大的缓冲区,但是可以使用较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的服务器,恢复的一种方法是仅仅拒绝导致内存用完“临时”错误的请求。 (“先来先服务”战略)
你不需要处理每一个new
exception:)例外可以传播。 devise你的代码,以便在每个“模块”中有处理错误的地方。
它依赖于编译器/运行时和你正在使用的operator new
(例如某些版本的Visual Studio 不会抛出包装盒 ,而是宁愿返回一个指向la malloc
的NULL
指针)。
你总是可以catch
一个std::bad_alloc
exception,或者显式地使用nothrow new
来返回NULL
而不是抛出。 (另请参阅围绕主题的过去的StackOverflow文章 。)
请注意,像malloc
这样的operator new
将在内存不足,地址空间不足(例如32位进程中的2-3GB(取决于操作系统)),超出配额(已提及ulimit
)或出连续的地址空间(例如碎片堆)。
osgx说:
任何真实世界的应用程序是否会检查大量的新闻,并在没有内存的情况下可以恢复?
我在回答 这个问题 时曾回答过这个问题 ,
处理这种情况是非常困难的。 您可能希望向应用程序的用户返回一个有意义的错误,但是如果这是由于内存不足造成的问题,则可能无法负担内存来分配错误消息。 这实际上有点捉襟见肘。
有一种防御性编程技术(有时称为记忆降落伞或雨天基金),当您的应用程序启动时,您可以在其中分配一大块内存。 当您处理bad_allocexception时,您可以释放这些内存,并使用可用的内存来优雅地closures应用程序,包括向用户显示一个有意义的错误。 这比崩溃好多了:)
是的, new
可以抛出std::bad_alloc
( std::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_alloc
exception。
如果new
引发exception,那么这是一个严重的错误:
- 超过可用虚拟机正在分配(最终失败)。 通过捕获
std::bad_alloc
exception,您可以尝试减less内存数量。