当我“扔”东西时,它存储在内存中的什么地方?

我知道当某个东西是 thrown 时,堆栈被“解除缠绕”到它被捕获的地方,并且在每个函数上下文中运行堆栈上类实例的析构函数(这就是为什么你不应该从析构函数中抛出一个异常——你可能最终抛出第二个异常) ... ... 但是我想知道在这种情况发生时,我抛出的对象在内存中存储在哪里?

它是否依赖于实现? 如果是,是否有最流行的编译器使用的特定方法?

2634 次浏览

我不知道这是否能回答你的问题,但是 This (C + + 编译器如何实现异常处理)是一篇关于异常处理的优秀文章: 。我强烈推荐(:

很抱歉我的回答很简短,但是这篇文章的整个信息都很棒,我不能在这里挑选和发布一些信息。

是的,答案取决于编译器。

对我的编译器(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));
}

我不知道这个计划有多典型。

来自 这一页:

异常需要存储 此存储必须持久存储 而堆栈正在解开,因为它 将由处理程序使用,并且必须 是线程安全的。 < strong > Exception 对象 因此,在一般情况下,储存 在堆 中分配,尽管 实施可能会提供一个 紧急缓冲区 支持投掷 低内存下 bad _ alloc 异常 条件。

现在,这只是安腾 ABI 和我正在寻找具体的细节,海湾合作委员会,Clang 和 MSVC。但是,该标准并没有指定任何内容,而且这似乎是实现异常存储的明显方法,所以..。

C + + 标准通常指定语言的行为方式,而不是编译器应该如何实现该行为。我认为这个问题属于那一类。实现这种功能的最佳方法取决于机器的具体情况——一些处理器有许多通用寄存器,而另一些只有很少的寄存器。处理器甚至可以只为异常构建一个特殊的寄存器,在这种情况下,编译器应该可以自由地利用这个特性。

它不能在栈上,因为它将被展开,而且它不能在堆上,因为这意味着系统可能不会抛出 std::bad_alloc。除此之外,它完全取决于实现: 没有指定实现(必须记录在案) ,但是没有指定。(一个实现可以在大多数情况下使用堆,只要它有某种紧急备份,即使在没有更多内存的情况下也允许有限数量的异常。)