c++ - 如果 'throw' 未能为异常对象分配内存会怎样?

标签 c++ c++11

来自 C++11 标准 (15.1.p4):

The memory for the exception object is allocated in an unspecified way, except as noted in 3.7.4.1

如果分配失败怎么办——它会抛出 std::bad_alloc 吗?调用 std::terminate?未指定?

最佳答案

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

我花了一些时间对此进行调查,这是我发现的:

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

Itanimum ABI 建议使用堆来处理异常:

Storage is needed for exceptions being thrown. This storage must persist while stack is being unwound, since it will be used by the handler, and must be thread-safe. Exception object storage will therefore normally be allocated in the heap

...

Memory will be allocated by the __cxa_allocate_exception runtime library routine.

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

它还提到了这一点:

If __cxa_allocate_exception cannot allocate an exception object under these constraints, it calls terminate()

是的...在 GCC 和 Clang 中“throw myX();”可以杀死您的应用程序,而您对此无能为力(也许编写自己的 __cxa_allocate_exception 会有所帮助 - 但它肯定不会是可移植的)

它变得更好:

3.4.1 Allocating the Exception Object

Memory for an exception object will be allocated by the __cxa_allocate_exception runtime library routine, with general requirements as described in Section 2.4.2. If normal allocation fails, then it will attempt to allocate one of the emergency buffers, described in Section 3.3.1, under the following constraints:

  • The exception object size, including headers, is under 1KB.
  • The current thread does not already hold four buffers.
  • There are fewer than 16 other threads holding buffers, or this thread will wait until one of the others releases its buffers before acquiring one.

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

结论:

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

您可以声称现代系统(即 Linux)无论如何都会在我们达到这种情况之前杀死此类服务器。但是(1)它不是一个论点; (2)内存管理器可以设置为overcommit; (3) 运行在 64 位硬件上且内存足够的 32 位应用(或应用人为限制内存分配)不会触发 OOM killer 。

就我个人而言,我对这个发现非常生气——多年来我一直声称我的代码可以优雅地处理内存不足。原来我对我的客户撒了谎。 :-( 还不如开始拦截内存分配,调用 std::terminate 并将所有相关函数视为 noexcept ——这肯定会让我的生活更轻松(编码方面).难怪他们仍然使用 Ada 来编程火箭。

关于c++ - 如果 'throw' 未能为异常对象分配内存会怎样?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45497684/

相关文章:

c++ - 按字母顺序排列字符串数组(甚至 *ptr)

c++ - 使用 pthread_t 作为映射中的键

c++ - 没有互斥锁的事件通知

c++ - 具有不同 C/C++ 运行时库的 exe 和 dll 之间的接口(interface)

c++ - 我可以只让孙类在 C++ 中实现一个方法吗?

c++ - 为什么要在 CMake 中使用包管理器?

c++ - 如何禁止分配给不引用变量?

c++ - 检查字符串是否在以字符串 vector 为值的映射的值中

c++ - 定义一个没有默认可构造数据成员的空默认构造函数

c++ - 如何将 std::string 分解为 std::string