我知道当throw
n为n时,堆栈将被“解卷”到被捕获的位置,并且在每个函数上下文中运行堆栈上的类实例的析构函数(这就是为什么您不应该从析构函数中引发异常) -您可能最终会抛出第二个)。但是我想知道发生这种情况时,我抛出的对象存储在内存中的什么位置?
是否依赖于实现?如果是这样,大多数流行的编译器是否使用特定的方法?
我知道当throw
n为n时,堆栈将被“解卷”到被捕获的位置,并且在每个函数上下文中运行堆栈上的类实例的析构函数(这就是为什么您不应该从析构函数中引发异常) -您可能最终会抛出第二个)。但是我想知道发生这种情况时,我抛出的对象存储在内存中的什么位置?
是否依赖于实现?如果是这样,大多数流行的编译器是否使用特定的方法?
raise
),销毁它。堆分配的问题是,如果尝试分配异常失败,该怎么办?您将不得不中止(就像由于空间不足而将其放到堆栈上“失败”时,堆栈溢出一样)。与Java不同,该实现不允许抛出其他异常。
Answers:
是的,答案取决于编译器。
通过对我的编译器(g++ 4.4.3
)进行的快速实验发现,其运行时库首先尝试malloc
为异常存储内存,否则,尝试在驻留在数据段上的整个进程“紧急缓冲区”内分配空间。如果仍然无法解决问题,则会呼叫std::terminate()
。
看来,紧急缓冲区的主要目的是能够std::bad_alloc
在进程用完堆空间后抛出(在这种情况下,malloc
调用将失败)。
相关功能是__cxa_allocate_exception
:
extern "C" void *
__cxxabiv1::__cxa_allocate_exception(std::size_t thrown_size) throw()
{
void *ret;
thrown_size += sizeof (__cxa_refcounted_exception);
ret = malloc (thrown_size);
if (! ret)
{
__gnu_cxx::__scoped_lock sentry(emergency_mutex);
bitmask_type used = emergency_used;
unsigned int which = 0;
if (thrown_size > EMERGENCY_OBJ_SIZE)
goto failed;
while (used & 1)
{
used >>= 1;
if (++which >= EMERGENCY_OBJ_COUNT)
goto failed;
}
emergency_used |= (bitmask_type)1 << which;
ret = &emergency_buffer[which][0];
failed:;
if (!ret)
std::terminate ();
}
// We have an uncaught exception as soon as we allocate memory. This
// yields uncaught_exception() true during the copy-constructor that
// initializes the exception object. See Issue 475.
__cxa_eh_globals *globals = __cxa_get_globals ();
globals->uncaughtExceptions += 1;
memset (ret, 0, sizeof (__cxa_refcounted_exception));
return (void *)((char *)ret + sizeof (__cxa_refcounted_exception));
}
我不知道这种方案有多典型。
malloc
通常,但不一定,“在这种情况下,呼叫将失败”。
从此页面:
需要存储以引发异常。解开堆栈时,该存储必须持久,因为它将由处理程序使用,并且必须是线程安全的。因此,异常对象存储通常将在堆中分配,尽管 实现可能会提供紧急缓冲区以支持在内存不足的情况下引发bad_alloc异常。
现在,这只是Itanium ABI,我在寻找有关GCC,Clang和MSVC的详细信息。但是,该标准未指定任何内容,这似乎是实现异常存储的显而易见的方法,因此...
我不知道这是否能回答您的问题,但是这篇(C ++编译器如何实现异常处理)是一篇关于异常处理的绝妙文章:我强烈推荐它(:
简短的答案很抱歉,但是本文中的全部信息非常棒,我无法在此处选择并发布一些信息。
excpt_info
在该页面中搜索,它提供了一些信息,说明MSVC是如何做到的。
好吧,它不能在堆栈上,因为它将被取消缠绕,也不能在堆栈上,因为那将意味着系统可能不会抛出std::bad_alloc
。除此之外,它完全取决于实现:未指定实现(必须记录在案),但未指定。(实现可以在大多数时间使用堆,只要它具有某种紧急备份,即使没有更多的内存也可以允许有限数量的异常。)
std::bad_alloc
有效。这意味着实现无法系统地使用堆。该标准不允许这样做。我将主张建立在标准之上。