72

There is a method called foo that sometimes returns the following error:

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Abort

Is there a way that I can use a try-catch block to stop this error from terminating my program (all I want to do is return -1)?

If so, what is the syntax for it?

How else can I deal with bad_alloc in C++?

jww
  • 97,681
  • 90
  • 411
  • 885
Nosrettap
  • 10,940
  • 23
  • 85
  • 140
  • 4
    Did you try to code a `try ... catch` statement? Which one?? – Basile Starynkevitch Feb 26 '12 at 20:15
  • I've never seen a bad_alloc exception. I imagine I'd have to allocate an incorrect amount of resources. What are you doing to cause that to happen? – Peter Wood Feb 26 '12 at 20:25
  • 2
    Can you be more specific and more precise? What do you mean with "there is", "sometimes", "all I want to do"... Catching exceptions is technically easy, but in most cases it's a bad idea to do so without understanding its semantics. – Wolf May 16 '14 at 14:33
  • Also the title is not consistent with your problem description. – Wolf May 16 '14 at 14:39

7 Answers7

101

In general you cannot, and should not try, to respond to this error. bad_alloc indicates that a resource cannot be allocated because not enough memory is available. In most scenarios your program cannot hope to cope with that, and terminating soon is the only meaningful behaviour.

Worse, modern operating systems often over-allocate: on such systems, malloc and new can return a valid pointer even if there is not enough free memory left – std::bad_alloc will never be thrown, or is at least not a reliable sign of memory exhaustion. Instead, attempts to access the allocated memory will then result in a segmentation fault, which is not catchable (you can handle the segmentation fault signal, but you cannot resume the program afterwards).

The only thing you could do when catching std::bad_alloc is to perhaps log the error, and try to ensure a safe program termination by freeing outstanding resources (but this is done automatically in the normal course of stack unwinding after the error gets thrown if the program uses RAII appropriately).

In certain cases, the program may attempt to free some memory and try again, or use secondary memory (= disk) instead of RAM but these opportunities only exist in very specific scenarios with strict conditions:

  1. The application must ensure that it runs on a system that does not overcommit memory, i.e. it signals failure upon allocation rather than later.
  2. The application must be able to free memory immediately, without any further accidental allocations in the meantime.

It’s exceedingly rare that applications have control over point 1 — userspace applications never do, it’s a system-wide setting that requires root permissions to change.1

OK, so let’s assume you’ve fixed point 1. What you can now do is for instance use a LRU cache for some of your data (probably some particularly large business objects that can be regenerated or reloaded on demand). Next, you need to put the actual logic that may fail into a function that supports retry — in other words, if it gets aborted, you can just relaunch it:

lru_cache<widget> widget_cache;

double perform_operation(int widget_id) {
    std::optional<widget> maybe_widget = widget_cache.find_by_id(widget_id);
    if (not maybe_widget) {
        maybe_widget = widget_cache.store(widget_id, load_widget_from_disk(widget_id));
    }
    return maybe_widget->frobnicate();
}

…

for (int num_attempts = 0; num_attempts < MAX_NUM_ATTEMPTS; ++num_attempts) {
    try {
        return perform_operation(widget_id);
    } catch (std::bad_alloc const&) {
        if (widget_cache.empty()) throw; // memory error elsewhere.
        widget_cache.remove_oldest();
    }
}

// Handle too many failed attempts here.

But even here, using std::set_new_handler instead of handling std::bad_alloc provides the same benefit and would be much simpler.


1 If you’re creating an application that does control point 1, and you’re reading this answer, please shoot me an email, I’m genuinely curious about your circumstances.

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • Except if the `try ... catch` is surrounding a naughty loop happening to allocate too many objects. – Basile Starynkevitch Feb 26 '12 at 20:17
  • 2
    @BasileStarynkevitch The question is not how to catch the error but how to proceed. If that loop is the centerpiece of your application and everything you do depends on its results, it's kinda hard to carry on without it. If you don't have plenty of memory you'd like to hold onto but can free, there's little use in trying again. If you can't try it again, there's little use in catching it. You may print out an error message and exit, but this should be left to `main` or other top-level code, it definitely shouldn't happen in library code. –  Feb 26 '12 at 20:22
  • 1
    I think if an exception leaves `main`, you don't get complete RAII cleanup. – Kerrek SB Feb 26 '12 at 20:26
  • @KerrekSB Interesting question, I assumed that cleanup happens *while* the stack is being unwound but it might make sense, and preserve correctness, to do it once the unwinding has been stopped. This would be problematic (because it essentially means that **every** program needs to catch **every** exception). But it’s still conceivable. – Konrad Rudolph Feb 26 '12 at 21:03
  • 1
    @Konrad: It's not like it's hard to catch all (C++) exceptions. :) – Xeo Feb 27 '12 at 04:12
  • @Xeo True. But I have to admit that I never do. Caveat: I only ever write console applications and I don’t think it’s a too big problem to percolate unhandled exceptions to the console. If I ever were to write a GUI application, I’d wrap `main` in a catch-all handler to log the exception and present an information dialog to the user. – Konrad Rudolph Feb 27 '12 at 09:38
  • 23
    @KonradRudolph, may be a bit too much saying "cannot and should not try" to respond to this error. What if that program is in a sensitive host (nuclear plant, robot, mars rover) where it is unacceptable to just get yourself killed by the exception? What if you _could_ actually handle it? An even simpler example is a game that is loading objects. If there is not enough memory, it could try reloading with lower quality meshes, or decrease the number of particles, or show cubes for whatever object that is missing etc. – Shahbaz Apr 15 '13 at 13:33
  • 2
    Indeed, I'd say you should most of the times catch and handle that exception, same way you would check for output of `malloc` not being `NULL` and handling it. – Shahbaz Apr 15 '13 at 13:33
  • @Shahbaz You cannot, that’s the long and the short of it. Same goes for `malloc` and `NULL`, incidentally. You won’t get meaningful diagnostics from this anyway, since most modern systems *never* fail a `malloc`, they just mark nonexistent memory as reserved. In a nuclear power plant etc. you won’t have exceptions anyway, you’ll have completely different checks, or run the process in isolation and simply kill it whole on the first sign of an error. But 99.999% of the readers here won’t ever work on such code and it’s silly to extrapolate from that. – Konrad Rudolph Apr 15 '13 at 15:41
  • 1
    @Shahbaz - Nuclear power plants and Mars rovers handle problems with memory allocation and exceptions in one of two ways: (1) It's not allowed. Period. That means saying goodbye to most of the C++ library. (2) It's allowed, but only at system startup where everything runs like clockwork every time and memory allocations can be monitored carefully. After startup, no allocation. Oh yes, no exceptions. The C++ library, if it exists, is a bit non-standard and more than a bit pared down. Exceptions, memory allocation, and high reliability computing don't mix. – David Hammen May 15 '14 at 13:43
  • 4
    @DavidHammen, that's true. Actually it would probably be written in C anyway, since many features of C++ are too high level for system programming and less controllable than in C. I meant to say that: saying "most often you can do nothing against bad_alloc, so you might as well get killed" is ok, but saying "you **should never** check for bad_alloc" is too much. You can't possibly imagine every use case. For some, maybe checking for bad_alloc is actually useful. – Shahbaz May 15 '14 at 14:12
  • @Shahbaz Sure but those people will know anyway. Statements are always implicitly qualified – otherwise words such as “never” would not have any right to existence, since there are always exceptions (oh, see here how hard it is to avoid absolutes in speech?). – Konrad Rudolph May 15 '14 at 14:15
  • 12
    @SHahbaz, One can always take the route Matlab used to take. Suppose a user inadvertently asks for almost all of virtual memory. Knowing the machine size, Matlab thought "I can do that! But these things must be done *delicately*!" It didn't go for all that memory at once. It instead went piece by piece. On getting a failure it went into a busy loop and retried, again and again. When some other process quit, Matlab gobbled that memory, ASAP. Eventually, there was nothing left but the Matlab beast, with no way to kill it other than reboot. It's not a good idea to catch allocation failures. – David Hammen May 15 '14 at 15:23
  • @DavidHammen, I'm sorry you had to deal with Matlab. I'm aware of how ... wonderful ... it is. But regardless, see another of my comments above that gives an example of where it _would_ be a good idea to catch allocation failures. – Shahbaz May 15 '14 at 16:39
  • Hmmm. If just a few more of us vote for the accepted answer this will be Konrad's seventh Populist badge. It's not a big stretch; the selected answer technically does answer the question, after all. – David Hammen May 15 '14 at 17:13
  • 3
    @KonradRudolph: "You won’t get meaningful diagnostics from this anyway, since most modern systems never fail a malloc, they just mark nonexistent memory as reserved." - just a thought, but perhaps this comment should be edited into the answer itself, as it's a crucial insight in to why attempts to handle `bad_alloc` may be useless or unreliable.... – Tony Delroy May 16 '14 at 00:55
  • 3
    "most modern systems never fail a malloc" - that's simply not true. Malloc and new can definitely fail on Windows, which is still the most widespread OS in 2015. Unfortunately supporting 32 bit applications is also still important for most vendors, and running out of virtual address space, or having memory fragmentation are common issues. (Btw. new can also fail with 64 bit applications on Windows.) – darklon Jan 15 '15 at 12:13
  • @CorneliusScarabeus I haven’t got numbers but as far as I know it’s the default behaviour on modern Linux and BSD derivates. Those definitely outnumber Windows (even when counting version numbers separately). Note I was counting systems, not market share. – Konrad Rudolph Jan 15 '15 at 13:00
  • I don't know exactly the specification of this error, however I just got it today on Linux (Suse and Kubuntu) when loading a huge chunk of memory ;) – kap Feb 07 '15 at 18:26
  • 1
    "In certain cases the program may attempt to free some memory and try again, " - or simply use another algorithm which uses less memory trading speed for smaller memory footprint. – TrueY May 08 '17 at 08:17
  • _"you cannot, and should not try, to respond to this error"_ - does it mean you should not provide exception safety guaranty? Because, sometimes, to provide exception safety (especially when providing strong guaranty) you have to catch the exception (and usually, after handling it, rethrow it further on stack). – anton_rh Nov 08 '19 at 06:41
  • Should you do graceful shutdown or just abort? Because to provide graceful shutdown you sometimes have to handle `bad_alloc`. RAII during stack unwinding may not be enough. Especially when there are multiple threads in your app (you have to gracefully stop other threads). "you should not respond to `bad_alloc`" sounds like you should just crash your app. – anton_rh Nov 08 '19 at 06:41
  • Another reason for not handling `bad_alloc`: [What happens if 'throw' fails to allocate memory for exception object?](https://stackoverflow.com/a/45552806/5447906). _gcc_ may crash your program if it cannot allocate memory for `bad_alloc`. But it may not be true for other compilers, though. _MSVC_ seems to use stack memory for exceptions. And _Windows_ doesn't use overcommit. So programs should be more stable on Window in low memory conditions. – anton_rh Nov 08 '19 at 06:47
  • @anton_rh For exception safety, would you ever need to handle `std::bad_alloc` specifically, rather than just catching everything and rethrowing? I can’t think of a situation but maybe I lack imagination. Regarding graceful shutdown: *if* you use proper RAII (and you should), then stack unwinding (a) is sufficient, and (b) can’t be aided by handling the exception. Depending on your circumstances graceful shutdown might still be more appropriate, but note that letting the app crash *also* has advantages — in particular, you get a proper stack trace you can then diagnose. – Konrad Rudolph Nov 08 '19 at 08:46
  • @anton_rh Regarding your last comment: implementations *should* reserve sufficient memory to throw `std::bad_alloc` without failing — but this can still fail if the exception is thrown while the stack is already being unwound (i.e. inside a `catch` block). – Konrad Rudolph Nov 08 '19 at 08:49
  • @KonradRudolph, _"would you ever need to handle std::bad_alloc specifically, rather than just catching everything and rethrowing?"_ in many cases (especially when you use _STL_) `std::bad_alloc` is only possible exception that can be thrown in block of code. Even if you write "`catch (...)`", you assume that it is only for `std::bad_alloc`. – anton_rh Nov 08 '19 at 09:17
42

What is the C++ Standard specified behavior of new in c++?

The usual notion is that if new operator cannot allocate dynamic memory of the requested size, then it should throw an exception of type std::bad_alloc.
However, something more happens even before a bad_alloc exception is thrown:

C++03 Section 3.7.4.1.3: says

An allocation function that fails to allocate storage can invoke the currently installed new_handler(18.4.2.2), if any. [Note: A program-supplied allocation function can obtain the address of the currently installed new_handler using the set_new_handler function (18.4.2.3).] If an allocation function declared with an empty exception-specification (15.4), throw(), fails to allocate storage, it shall return a null pointer. Any other allocation function that fails to allocate storage shall only indicate failure by throw-ing an exception of class std::bad_alloc (18.4.2.1) or a class derived from std::bad_alloc.

Consider the following code sample:

#include <iostream>
#include <cstdlib>

// function to call if operator new can't allocate enough memory or error arises
void outOfMemHandler()
{
    std::cerr << "Unable to satisfy request for memory\n";

    std::abort();
}

int main()
{
    //set the new_handler
    std::set_new_handler(outOfMemHandler);

    //Request huge memory size, that will cause ::operator new to fail
    int *pBigDataArray = new int[100000000L];

    return 0;
}

In the above example, operator new (most likely) will be unable to allocate space for 100,000,000 integers, and the function outOfMemHandler() will be called, and the program will abort after issuing an error message.

As seen here the default behavior of new operator when unable to fulfill a memory request, is to call the new-handler function repeatedly until it can find enough memory or there is no more new handlers. In the above example, unless we call std::abort(), outOfMemHandler() would be called repeatedly. Therefore, the handler should either ensure that the next allocation succeeds, or register another handler, or register no handler, or not return (i.e. terminate the program). If there is no new handler and the allocation fails, the operator will throw an exception.

What is the new_handler and set_new_handler?

new_handler is a typedef for a pointer to a function that takes and returns nothing, and set_new_handler is a function that takes and returns a new_handler.

Something like:

typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();

set_new_handler's parameter is a pointer to the function operator new should call if it can't allocate the requested memory. Its return value is a pointer to the previously registered handler function, or null if there was no previous handler.

How to handle out of memory conditions in C++?

Given the behavior of newa well designed user program should handle out of memory conditions by providing a proper new_handlerwhich does one of the following:

Make more memory available: This may allow the next memory allocation attempt inside operator new's loop to succeed. One way to implement this is to allocate a large block of memory at program start-up, then release it for use in the program the first time the new-handler is invoked.

Install a different new-handler: If the current new-handler can't make any more memory available, and of there is another new-handler that can, then the current new-handler can install the other new-handler in its place (by calling set_new_handler). The next time operator new calls the new-handler function, it will get the one most recently installed.

(A variation on this theme is for a new-handler to modify its own behavior, so the next time it's invoked, it does something different. One way to achieve this is to have the new-handler modify static, namespace-specific, or global data that affects the new-handler's behavior.)

Uninstall the new-handler: This is done by passing a null pointer to set_new_handler. With no new-handler installed, operator new will throw an exception ((convertible to) std::bad_alloc) when memory allocation is unsuccessful.

Throw an exception convertible to std::bad_alloc. Such exceptions are not be caught by operator new, but will propagate to the site originating the request for memory.

Not return: By calling abort or exit.

ThomasMcLeod
  • 7,603
  • 4
  • 42
  • 80
Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • 6
    This does not directly answer the exact Q asked, (Konrad's answer already answers it quite well) But this answer provides a insight on less commonly known means of handling out of memory conditions in c++ before a `bad_alloc`is thrown. – Alok Save Feb 27 '12 at 03:46
  • this permits to setup your own error handler and display if needed a stack backtrace and thus locate enough precisely where is the problem. – sancelot Jul 02 '14 at 14:14
  • That doesn't work for me, the program still doesn't get my call catched and freeze instead (arch linux) if the swap is disabled – Ingo Mi Feb 25 '20 at 22:29
  • @IngoMi you could take a look at Chapter 8 (Customizing new and delete) of Effective C++ by Scott Meyers to see whether your implementation is correct. – Hari Mar 16 '23 at 12:22
41

You can catch it like any other exception:

try {
  foo();
}
catch (const std::bad_alloc&) {
  return -1;
}

Quite what you can usefully do from this point is up to you, but it's definitely feasible technically.

Flexo
  • 87,323
  • 22
  • 191
  • 272
9

I would not suggest this, since bad_alloc means you are out of memory. It would be best to just give up instead of attempting to recover. However here is is the solution you are asking for:

try {
    foo();
} catch ( const std::bad_alloc& e ) {
    return -1;
}
Wolf
  • 9,679
  • 7
  • 62
  • 108
Sam Miller
  • 23,808
  • 4
  • 67
  • 87
  • 2
    @Wolf: the question explicitly says "to stop this error from terminating my program (all I want to do is return -1)" - it may or may not be useful as discussed in Konrad's answer, but this is what's been requested (unlike your answer's `exit()`). – Tony Delroy May 16 '14 at 00:50
  • @TonyD Yes, you're right, and he explicitly states that he doesn't suggest it. Today I would not downvote this. **My technical problem** is now that that I cannot change this premature decision any more, because it happened too long ago. – Wolf May 16 '14 at 12:59
  • SamMiller do you think it's possible to improve your answer so that it can compete with [Flexo's](http://stackoverflow.com/a/9456754/2932052) ...I'd really like to (at least) undownvote it ;) – Wolf May 16 '14 at 13:05
  • I already made a [suggestion](http://stackoverflow.com/review/suggested-edits/4845202) ...which was accepted :) – Wolf May 19 '14 at 07:57
5

I may suggest a more simple (and even faster) solution for this. new operator would return null if memory could not be allocated.

int fv() {
    T* p = new (std::nothrow) T[1000000];
    if (!p) return -1;
    do_something(p);
    delete p;
    return 0;
}

I hope this could help!

Ajay
  • 18,086
  • 12
  • 59
  • 105
TrueY
  • 7,360
  • 1
  • 41
  • 46
1

Let your foo program exit in a controlled way:

#include <stdlib.h>     /* exit, EXIT_FAILURE */

try {
    foo();
} catch (const std::bad_alloc&) {
    exit(EXIT_FAILURE);
}

Then write a shell program that calls the actual program. Since the address spaces are separated, the state of your shell program is always well-defined.

Wolf
  • 9,679
  • 7
  • 62
  • 108
1

Of course you can catch a bad_alloc, but I think the better question is how you can stop a bad_alloc from happening in the first place.

Generally, bad_alloc means that something went wrong in an allocation of memory - for example when you are out of memory. If your program is 32-bit, then this already happens when you try to allocate >4 GB. This happened to me once when I copied a C-string to a QString. The C-string wasn't '\0'-terminated which caused the strlen function to return a value in the billions. So then it attempted to allocate several GB of RAM, which caused the bad_alloc.

I have also seen bad_alloc when I accidentally accessed an uninitialized variable in the initializer-list of a constructor. I had a class foo with a member T bar. In the constructor I wanted to initialize the member with a value from a parameter:

foo::foo(T baz) // <-- mistyped: baz instead of bar
: bar(bar)
{
}

Because I had mistyped the parameter, the constructor initialized bar with itself (so it read an uninitialized value!) instead of the parameter.

valgrind can be very helpful with such errors!

Algoman
  • 1,905
  • 16
  • 16