Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ memory management paradigms

I'm moving from C to C++11 and trying to figure out the memory management paradigm for C++11 programs (or any modern languages with built-in exceptions). Specifically, I'm having a crack at game development where running out of memory is a real concern.

In C, I'm using to checking the return value of malloc; and generally use custom allocators.

With C++, I'm quite confused; though I like how the STL containers were built allowing custom allocators. Since the STL containers all manage their own memory, simply adding an element to a vector could throw a std::bad_alloc. How do I guard against such things? I have heard wrapping all throwing calls in try/catch blocks can be prohibitively expensive.

However, allowing the exception to travel up the callstack would allow in a bunch of functions that would not fully execute, and would lead to some really tricky code. i.e. if A->B->C->D is a callstack, D throws, and A catches, then B, C, and D could have potentially created some weird problems by not being able to finish execution normally.

Additionally, the nothrow argument seems to allow very C-like code; though I now don't see the benefit over a plain malloc.

What are some best practices out there for writing exception-safe C++ code that guards against out-of-memory issues?

edit: A relevant answer on progammers.stackexchange arguing for exception-less C++ design in consoles. Not sure if these arguments still apply to the 8th generation consoles

like image 248
zac Avatar asked Oct 01 '14 03:10

zac


People also ask

What is memory management in C language?

Dynamic memory management in C programming language is performed using the malloc() , calloc() , realloc() , and free() functions. These four functions are defined in the <stdlib.h> C standard library header file. It uses the heap space of the system memory.

How many memory management functions are there in C?

There are 4 library functions provided by C defined under <stdlib.h> header file to facilitate dynamic memory allocation in C programming. They are: malloc() calloc()

What are the memory types in C?

C Memory Model The C runtime memory model can be divided in to three types; global/static memory, the heap, and the stack. These all share the RAM available on the microcontroller.

What are the two types of memory allocation in C?

There are two types of memory allocations. Static and dynamic.


2 Answers

My answer will be geared more towards game development since that is my background and that is part of what you are interested in. Different types of applications will have different requirements.

Games generally allocate all dynamic memory up front and stick within that budget. Consoles in particular have hard memory limits and most games will want to use all of it.

There's a few reasons for allocating everything up front.

One, performance. Memory allocation is slow. You want to avoid it at all costs. If you allocate everything up front, you can then write custom, high performance memory allocators like Pool Allocators, Stack Allocators, etc, that just grab memory from your pre-allocated buffer. It's important to choose the best allocator for the task at hand.

Two, you'll know quickly if there isn't enough memory for your game. In development, you'll crash if you run out of memory and will need to adjust usage, but the final release shouldn't crash because you've allocated up front and stuck within your memory budgets.

For exceptions, many (but not all) games disable exceptions, again for performance reasons. In fact some console compilers don't even support exceptions. Then you will either need to use an STL library with no exceptions, or implement your own containers. Many game teams choose to implement their own for performance reasons as well as to better integrate them with custom memory allocators.

That said, dynamic memory allocation, STL, and exceptions are probably perfectly fine for smaller personal projects/games, but keep in mind what will be necessary for large, high performance, real-time games.

For exception safety, I would definitely use RAII. That is its purpose. Also I would recommend using smart pointers like std::unique_ptr and std::shared_ptr for memory management. Coupled with RAII, if your constructors throw, memory will be freed.

like image 168
megadan Avatar answered Oct 21 '22 22:10

megadan


Use destructors to clean up automatically when scopes are exited. That's called RAII, Resource Acquisition Is Initializion, although the acronym is not exactly the best one could devise. All the standard containers etc. clean up automatically.

In languages like C# and Java which are based on garbage collection, you instead pepper the code with try blocks and “using” statements. Java just got that (part of the try syntax IIRC); it's been in C# from the beginning (keyword using); in Python it's called with; and C++ doesn't have it and doesn't need it. I once created a WITH macro for C++, a clever little hack, thinking I would be using it all the time, but I haven't used it once except just to try it out right after creating it: in C++ RAII does it all.

Summing up: use RAII, i.e., use destructors, and just let those exceptions propagate.


Regarding memory exhaustion, ordinarily that's just regarded as “we're done for”, nothing to do except terminate as orderly as possible.

But it doesn't hurt to maybe set aside a little buffer which can deallocated when memory is exhausted, so as to have some working memory for cleanup-work.


C++ doesn't differentiate between hard exceptions (fatal like e.g. memory exhaustion) and soft exceptions (general failures that are not fatal).

like image 39
Cheers and hth. - Alf Avatar answered Oct 21 '22 20:10

Cheers and hth. - Alf