How to Deal with Bad_Alloc in C++

How to deal with bad_alloc in C++?

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.

c++ bad_alloc exception not catched

Your new operator has to get the memory from somewhere. As new is user space code that has no connection to real memory whatsoever, all that it can do is to ask the kernel via the syscall sbrk() or the syscall mmap() for some memory. The kernel will respond by mapping some additional memory pages into your process's virtual address space.

As it happens, any memory pages that the kernel returns to user processes must be zeroed out. If this step were skipped, the kernel might leak sensible data from another application or itself to the userspace process.

Also it happens, that the kernel always has one memory page that contains only zeros. So it can simply fulfill any mmap() request by simply mapping this one zero page into the new address range. It will mark these mapping as Copy-On-Write, so that whenever your userspace process starts writing to such a page, the kernel will immediately create a copy of the zero page. It is then that the kernel will grope around for another page of memory to back its promises.

You see the problem? The kernel does not need any physical memory up unto the point where your process actually writes to the memory. This is called memory over-committing. Another version of this happens when you fork a process. You think the kernel would immediately copy your memory when you call fork()? Of course not. It will just do some COW-mappings of the existing memory pages!

(This is an important optimization mechanism: Many mappings that are initiated never need to be backed by additional memory. This is especially important with fork(): This call is usually immediately followed by an exec() call, which will immediately tear down the COW-mappings again.)

The downside is, that the kernel never knows how much physical memory it actually needs until it fails to back its own promises. That is why you cannot rely on sbrk() or mmap() to return an error when you run out of memory: You don't run out of memory until you write to the mapped memory. No error code return from the syscall means that your new operator does not know when to throw. So it won't throw.

What happens instead is that the kernel will panic when it realizes that it has run out of memory, and start shooting down processes. That is the job of the aptly named Out-Of-Memory killer. That's just to try to avoid rebooting immediately, and, if the heuristics of the OOM-killer work well, it will actually shoot the right processes. The killed processes won't get so much as a warning, they are simply terminated by a signal. No user-space exception involved, again.


TL;DR: Catching bad_alloc exceptions on an over-committing kernel is next to useless.

Manage bad_alloc exception in C++ construtor

If an exception is thrown during the construction of A, your destructor will not be called.

Obviously the solution depends on what you're doing, but ideally you won't have to do any cleaning up. You should utilize RAII, and your class members should clean-up themselves.

That is, don't use any pointers raw; wrap them up and let the wrapper take care of it. Surprise! C++ programmers hate memory management just like you. We like to wrap it up and forget about it.

If you truly need to, though, I think this is common:

struct foo
{
int* i;
some_member_that_could_throw crap;

foo() // do *not* new i! if the second member throws, memory is leaked.
{ // rather:

// okay we made it, the other member must have initialized
i = new int;
}
};

Concerning your pointer, it's value remains unchanged. When new throws an exception (for whatever reason), the stack is unwound. The rest of the expression is abandoned.


Here's how exceptions and object creation will work. It's a recursive process, because each member or base class will in turn follow this list. Fundamental types have no constructors; this is the base case for the recursion.

  1. First, construct each of our base classes. (Which in turn run this list.)
  2. Initialize members of the class, one by one.
  3. Run the constructor body.
  4. finish with a fully constructed object.

Obviously, if item 1 fails there isn't any cleaning up for us to do, as none of our members have been initialized. We're good there.

Two is different. If at any point one of those fails to construct , the initialized members so far will be destructed, then the constructor will stop progress and the exception goes on it's merry way. This is why when you let your members clean up after themselves you have nothing to worry about. The uninitialized have nothing to do, and the initialized are going to have their destructors run, where cleanup occurs.

Three even more so. now that your objects are fully initialized, you're guaranteed they will all have their destructors run. Again, wrap things up and you have nothing to worry about. However if you have a raw pointer lying around, this is the time for a try/catch block:

try
{
// some code
}
catch (..) // catch whatever
{
delete myrawPointer; // stop the leak!
throw; // and let the exception continue
}

It's much messier to write exception-safe code without RAII.

How to deal with bad_alloc in RAII?

If you get a bad_alloc you're pretty much screwed anyway. I'm not sure how you expect to handle the allocation failing here. Not using exceptions does not really apply in this case.

If you really want to opt out of it you can add a nothrow to that statement and it'll return nullptr instead of throwing a bad_alloc:

shared_ptr<A> aPtr(new (std::nothrow) A());

For more information see this question about the design consideration involved. Additionally see this question explainign why using std::nothrow is a bad idea.

I am getting bad_alloc error in c++ program

The problem is in the treatment of the variable x. Don't ask me what the solution is but I can describe the problem.

        while (x--) {
vect.push_back(vect[i] / 2);
l++;
}

The first time this loop runs x equals two. So two items are added to the vector and x takes the value -1.

The next time this loop runs x starts at -1 and so the loop runs until you run out of memory and get a bad_alloc exception.

How to fix 'std::bad_alloc'

Your first problem is in your initialization.

int lunghezza;

Queue(int l){
lunghezza=l;
};
Pila<V> A= Pila <V>(lunghezza);
Pila<V> B= Pila <V>(lunghezza);

A and B will be initialized with the value of lunghezza which is not yet initialized. This way you can get a very high value that allocate lots of memory.

A better implementation of this part is to use initializers

int lunghezza;

Queue(int l) : lunghezza(l), A(l), B(l)
{
}

Pila<V> A;
Pila<V> B;

You second problem is in Pila class.

Pila(int L){
lunghezza=L;
Tab= new T[L];
cima=lunghezza;
}

In this code, Tab is never disallocated so you will leak (loose) the allocated memory when your object will be destroyed. You must call delete[] Tab; in your destructor.

You can also use STL classes such as std::list or std::vector to replace your class Pila.

What is the most common reason that bad_alloc is thrown?

EDIT: The other commenters have pointed out a few interesting scenarios. I'm adding them to my response for the sake of completeness.

Case 1: Running out of memory

My understanding is that bad_alloc is thrown whenever the operators new and new[] fail to allocate memory to an object or variable. This can happen if you've newed a bunch of objects and forgot to delete them before they got out of scope (i.e., your code leaks like crazy).

Case 2: Allocating huge amounts of memory in one swoop

Allocating a large chunk of memory, as in the case of a 1000 x 1000 x 1000 matrix of doubles, will possibly fail because it requires a single block of that size.

There might be several free memory blocks available, but none of these are large enough.

Case 3: Passing an invalid value to new[]

bad_alloc is thrown if you pass a negative value as its parameter.



Related Topics



Leave a reply



Submit