如果'throw'无法为异常对象分配内存会发生什么?

C.M*_*.M. 35 c++ c++11

从C++ 11标准(15.1.p4):

除非在3.7.4.1中指出,否则异常对象的内存以未指定的方式分配

如果分配失败怎么办 - 它会抛出std::bad_alloc吗?打电话std::terminate?未指定?

C.M*_*.M. 11

(提供我自己的答案......我会等几天,如果没有问题 - 我会将其标记为已接受)

我花了一些时间来研究这个,这就是我发掘出来的:

  • C++标准没有规定在这种情况下会发生什么
  • Clang和GCC似乎使用C++ Itanium ABI

Itanimum ABI建议将堆用于异常:

抛出异常需要存储.这个存储必须在堆栈被解除时保持不变,因为它将由处理程序使用,并且必须是线程安全的.因此,异常对象存储通常将在堆中分配

...

内存将由__cxa_allocate_exception运行时库例程分配.

所以,是的...抛出异常可能涉及锁定互斥锁并搜索空闲内存块.:-(

它还提到了这个:

如果__cxa_allocate_exception无法在这些约束下分配异常对象,则调用terminate()

是的...在GCC和Clang"扔myX();" 可以杀死你的应用程序,你不能做任何事情(也许写你自己__cxa_allocate_exception可以帮助 - 但它肯定不会是便携式的)

它变得更好:

3.4.1分配异常对象

异常对象的内存将由__cxa_allocate_exception运行时库例程分配,其一般要求如第2.4.2节中所述.如果正常分配失败,那么它将尝试在以下约束条件下分配第3.3.1节中描述的紧急缓冲区之一:

  • 异常对象大小(包括标题)小于1KB.
  • 当前线程尚未包含四个缓冲区.
  • 有少于16个其他线程持有缓冲区,或者此线程将等到其中一个线程在获取缓冲区之前释放其缓冲区.

是的,你的程序可以简单地挂起!这种情况很小 - 你需要耗尽内存并且线程需要耗尽所有16个紧急缓冲区并进入等待另一个应该生成异常的线程.但是如果你做的事情std::current_exception(比如链接异常并在线程之间传递它们) - 它不是那么不可能.

结论:

这是C++标准的缺陷 - 您无法编写100%可靠的程序(使用异常).教科书示例是一个服务器,它接受来自客户端的连接并执行提交的任务.处理问题的明显方法是抛出异常,这将解除所有内容并关闭连接 - 所有其他客户端不会受到影响,服务器将继续运行(即使在低内存条件下).唉,这样的服务器不可能用C++编写.

你可以声称现代系统(即Linux)会在我们达到这种情况之前杀死这样的服务器.但是(1)它不是一个论点; (2)内存管理器可以设置为overcommit; (3)在具有足够内存的64位硬件上运行的32位应用程序不会触发OOM杀手(或者如果app人为限制内存分配).

就个人而言,我对这一发现感到非常生气 - 多年来我声称我的代码优雅地处理了内存不足.结果我骗了我的客户.:-(还不如开始截取内存分配,调用std::terminate和处理所有相关的功能noexcept-这肯定会让我的生活更轻松(编码明智)难怪他们仍然使用的Ada程序火箭.

  • @BoPersson 这是因为他们的代码中有一个错误。这种咆哮的要点是,对于 C++,即使是 100% **正确**的代码仍然可能崩溃。 (2认同)
  • @André afaik 标准允许在这种情况下的任何行为 - 因此 Itanium ABI 没问题。但由于“任何行为”意味着“无法用于实际目的”——我认为这是 C++ 标准的缺陷。 (2认同)