What Happens If 'Throw' Fails to Allocate Memory for Exception Object

What happens if 'throw' fails to allocate memory for exception object?

(providing my own answer... I'll wait for few days and if there are no problems with it -- I'll mark it as accepted)

I spent some time investigating this and here is what I unearthed:

  • C++ standard does not specify what is going to happen in this case
  • Clang and GCC seem to use C++ Itanium ABI

Itanimum ABI suggests to use heap for exceptions:

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.

So, yeah... throwing an exception will likely involve locking mutexes and searching for a free memory block. :-(

It also mentions this:

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

Yep... in GCC and Clang "throw myX();" can kill your app and you can't do a thing about it (maybe writing your own __cxa_allocate_exception can help -- but it certainly won't be portable)

It gets even better:

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.

Yep, your program can simply hang! Granted chance of this are small -- you'd need to exhaust memory and your threads need to use up all 16 emergency buffers and enter wait for another thread that should generate an exception. But if you do things with std::current_exception (like chaining exception and passing them between threads) -- it is not so improbable.

Conclusion:

This is a deficiency in C++ standard -- you can't write 100% reliable programs (that use exceptions). Text book example is a server that accepts connections from clients and executes submitted tasks. Obvious approach to handle problems would be to throw an exception, which will unwind everything and close connection -- all other clients won't be affected and server will continue to operate (even under low memory conditions). Alas, such server is impossible to write in C++.

You can claim that modern systems (i.e. Linux) will kill such server before we reach this situation anyway. But (1) it is not an argument; (2) memory manager can be set to overcommit; (3) OOM killer won't be triggered for 32-bit app running on 64bit hardware with enough memory (or if app artificially limited memory allocation).

On personal note I am quite pissed about this discovery -- for many years I claimed that my code handles out-of-memory gracefully. Turns out I lied to my clients. :-( Might as well start intercepting memory allocation, call std::terminate and treat all related functions as noexcept -- this will certainly make my life easier (coding-wise). No wonder they still use Ada to programs rockets.

Exception throw from constructor and what happen to instance and memory allocation?

Throwing exceptions in the constructor is not a problem to garbage collector, and it definitely does not result in memory leaks.

Although the memory allocated from the heap never makes it to your program, it is available to the internal implementation of the operator new, which takes care of ensuring that the newly created instance becomes eligible for garbage collection.

How does failure to allocate memory in constructor bubble up

In this case an exception is thrown.

The new(nothrow) only means that failure to allocate storage for the object will not throw, it does not place constraint on the object constructor (nor constructors of subobjects).

What happens to heap allocated memory when exceptions occur during the constructing 'new' objects?

It just works. The memory will be released before your exception handling block is executed.

Is memory allocated for an object automatically deleted if an exception is thrown in a constructor?

Yes, the memory allocated for the CFoo object will be freed in this case.

Because the exception due to the failed allocation causes the CFoo constructor to fail to complete successfully the new-expression is guaranteed to free the memory allocated for that CFoo object.

This guarantee is specified in 5.3.4 [expr.new] / 17 of ISO/IEC 14882:2003.

Note, that it is always advisable to assign the result of a dynamic allocation to a smart pointer to ensure proper clean up. For example, if there was further code in CFoo constructor and that threw an exception the CBar object already successfully allocated earlier in the constructor would be leaked.

Memory Allocation Exception in Constructor

The memory to which a and b point would not be automatically freed. Every new[] must be balanced explicitly with a delete[].

Even if your destructor performed the deletion (assuming a, b, and c are class members), then you'd still leak memory. That's because the destructor wouldn't be called in this case since the object failed to construct.

Using std::vectors would obviate these problems.

Who deletes the memory allocated during a new operation which has exception in constructor?

You should refer to the similar questions here and here.
Basically if the constructor throws an exception you're safe that the memory of the object itself is freed again. Although, if other memory has been claimed during the constructor, you're on your own to have it freed before leaving the constructor with the exception.

For your question WHO deletes the memory the answer is the code behind the new-operator (which is generated by the compiler). If it recognizes an exception leaving the constructor it has to call all the destructors of the classes members (as those have already been constructed successfully prior calling the constructor code) and free their memory (could be done recursively together with destructor-calling, most probably by calling a proper delete on them) as well as free the memory allocated for this class itself. Then it has to rethrow the catched exception from the constructor to the caller of new.
Of course there may be more work which has to be done but I cannot pull out all the details from my head because they are up to each compiler's implementation.

Hinnant's stack allocator and exceptions

The gcc and clang implementations follow a specification called the Itanium ABI. It says, among other things:

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, although implementations may provide an emergency buffer to support throwing bad_alloc exceptions under low memory conditions (see Section 3.3.1).

Memory will be allocated by the __cxa_allocate_exception runtime library routine. This routine is passed the size of the exception object to be thrown (not including the size of the __cxa_exception header), and returns a pointer to the temporary space for the exception object. It will allocate the exception memory on the heap if possible. If heap allocation fails, an implementation may use other backup mechanisms (see Section 3.4.1).

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

The libc++abi implementation of __cxa_allocate_exception is here. It will first go to the heap, and if that fails, try an emergency backup buffer that is statically allocated within the libc++abi.dylib. If both the heap, and the emergency-stash fail to allocate enough memory for the arbitrary-sized user-created exception, terminate() is called.

What happens to the memory allocated by `new` if the constructor throws?

The memory will be automatically freed before the exception propagates.

This is essential, because a) the program never receives a pointer to free, and b) even if it did, it would have no portable way to actually free it since the memory never became an object that you can delete.



Related Topics



Leave a reply



Submit