Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When I `throw` something, where is it stored in memory?

I understand that when something is thrown, the stack is 'unwound' to the point where it is caught, and the destructors of class instances on the stack in each function context are run (which is why you should not throw an exception from a destructor - you could end up throwing a second one)...but I wonder where in memory the object that I have thrown is stored while this happens?

Is it implementation dependent? If so, is there a particular method used by most popular compilers?

like image 455
sje397 Avatar asked Jul 07 '11 14:07

sje397


People also ask

Where is heap memory located?

Stored in computer RAM just like the stack.

Where are exceptions allocated C++?

"When an exception is thrown, the exception object is created and an placed generally on some sort of exception data stack" (Stanley Lippman, "Inside the C++ Object Model" - 7.2 Exception handling)


1 Answers

Yes, the answer is compiler-dependent.

A quick experiment with my compiler (g++ 4.4.3) reveals that its runtime library first tries to malloc memory for the exception and, failing that, attempts to allocate space within a process-wide "emergency buffer" that lives on the data segment. If that doesn't work out, it calls std::terminate().

It would appear that the main purpose of the emergency buffer is to be able to throw std::bad_alloc after the process has run out of heap space (in which case the malloc call would fail).

The relevant function is __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)); } 

I don't know how typical this scheme is.

like image 93
NPE Avatar answered Oct 12 '22 22:10

NPE