23
\$\begingroup\$

I am aware of several games which are written in C++ but do not use exceptions. Since handling of memory allocation failure in C++ is generally built around the std::bad_alloc exception, how do these games handle such a failure?

Do they simply crash, or is there another way to handle and recover from an out-of-memory error?

\$\endgroup\$
  • 1
    \$\begingroup\$ Note that there are certain environments/OSs where the allocation claims to be successful, but trying to use the memory crashes your (or other) program \$\endgroup\$ – PlasmaHH Aug 7 '16 at 21:30
  • 6
    \$\begingroup\$ If allocation fails during a game, Quake 3 will drop you back to the menu with an error message. I believe this is made possible by Quake 3's pool allocator, which can just drop the entire pool and return to the menu safely if the pool runs out. \$\endgroup\$ – Dietrich Epp Aug 8 '16 at 2:08
  • 16
    \$\begingroup\$ Usually, by crashing. \$\endgroup\$ – immibis Aug 8 '16 at 3:08
  • 1
    \$\begingroup\$ If exceptions are genuinely disabled, the program must instead call std::terminate, and that's it. But then under the same restriction, allocation can return a null pointer instead of throwing an exception, and that result can be checked and handled separately. \$\endgroup\$ – underscore_d Aug 8 '16 at 10:36
  • 2
    \$\begingroup\$ In C++, you can say ClassName variableName = new(nothrow) ClassName(); (obviously replacing class name, variable name, etc.) Then, if allocation fails, you can detect it by saying if(!variableName) allowing the error to be handled without a try-catch exception block. Likewise, if the memory is allocated using a function like malloc(), calloc(), etc., then failures to allocate can be detected using the same if(!variableName) method without needing a try-catch. As for how the games handle these errors, well, at that point, it is up to the game devs to decide if it crashes or not. \$\endgroup\$ – Spencer D Aug 8 '16 at 17:09
63
\$\begingroup\$

Same as all average programs: They don't.*

For most applications there is no customer expectation that they continue working once memory runs out. All games fall under these "most applications". Spending time and money to work on an edge case which the customer doesn't expect to work doesn't make sense.

The question is similar to the following:

  • How do you install a game if the harddisk is full?
  • How do you still run the game at high fps on a PC below minimum specs?

The answer is the same: These are the users' problems. The game doesn't care.


*Actually, they usually do a high level catch of the exception, and use memory that was pre-allocated at the start of the game in order to try to log the event before crashing/terminating. The log then allows customer support to waste less time on the issue.

\$\endgroup\$
23
\$\begingroup\$

Typically this kind of scenario never happens.

Firstly, virtual memory on modern operating systems means that it's highly unlikely to happen in normal operation anyway; unless you have a runaway allocation bug, the game will be allocating memory from the OSs virtual address space and the OS will be looking after paging in and out.

That's all well and good, but it doesn't really apply to consoles or older OSs, so secondly, games don't even do lots of small dynamic allocations using standard library allocators anyway. Instead what they'll do is allocate a large pool of memory one-time-only at startup, and draw down from that pool for any runtime allocations which are required (e.g. by using C++ placement new or by writing their own memory management system on top of it).

That also protects against memory leaks because rather than having to track and account for each small individual allocations, a game can just throw away the entire pool to reclaim memory.

And thirdly, games will always set a minimum specification and budget their memory usage within that. So if a game is specified to require 1gb of RAM, that means that so long as its memory usage never exceeds 1gb, it never needs to worry about running out of RAM.

\$\endgroup\$
  • 3
    \$\begingroup\$ "Instead what they'll do is allocate a large pool of memory one-time-only at startup, and draw down from that pool for any runtime allocations which are required" - and what do you think happens if the pool is full? \$\endgroup\$ – immibis Aug 8 '16 at 3:08
  • \$\begingroup\$ @immibis - see my third point please. \$\endgroup\$ – Maximus Minimus Aug 8 '16 at 8:17
  • 2
    \$\begingroup\$ @immibis You mean ... what do they do if the pool is empty? Unlike system memory, the size and usage of the pool is completely under the application's control. So the pool cannot become empty unless the application has a bug. \$\endgroup\$ – David Schwartz Aug 8 '16 at 9:39
  • \$\begingroup\$ @DavidSchwartz Or unless the kernel is using memory overcommit. Linux does this. Even zeroing out your pool doesn't help if pagefile compression is enabled via zswap or zram. \$\endgroup\$ – Damian Yerrick Aug 8 '16 at 14:27
  • 1
    \$\begingroup\$ This doesn't seem to answer the actual question but instead states something similar to "This ship is unsinkable so what would we need an emergency procedure for?" \$\endgroup\$ – Lilienthal Aug 9 '16 at 11:12
2
\$\begingroup\$

Well, mainly, the same way we did it before exceptions existed - the old "check the return value approach". If an allocator doesn't use exceptions, it will usually return null when an allocation fails. A well written game will check for that and handle the situation in whatever way is safe. Sometimes, this means terminating the game (e.g. std::terminate) or at least reverting to last known safe state (e.g. when you allocate from a pool, you can safely dispose of the whole pool even when it's in an unsafe state).

Many games do use exceptions for cases like this even then. When someone says "avoid using exceptions in game code", what they usually mean is "only use exceptions for truly exceptional cases, not for mundane flow control". The setup for catching an out of memory exception is cheap - the cost is mostly in the throw, and the complications you get with handling the exception. Neither of those is very important in a typical out of memory case - you almost always want to terminate anyway.

Mind you, most games will just crash. This isn't as crazy as it sounds - you usually have some memory usage as a target, and make sure your game doesn't reach that limit (for example, by using a unit count limit in an RTS game, or by limiting the amount of enemies in a section of an FPS). Even if you don't, you usually don't run out of memory on any reasonably modern system - you run out of virtual address space (in a 32-bit application) or the stack, for example. The OS already pretends that you have as much memory as you ask for - that's one of the abstractions virtual memory provides. The point is, just because you have 256 MiB of physical RAM doesn't mean that your application can't use 4 GiB of memory. Some games might not even mind too much that all of their data isn't in physical RAM all the time (as long as the application doesn't need to touch that data, it can stay on disk, for example).

\$\endgroup\$
1
\$\begingroup\$

Catching an exception and Deciding to do what when and exception is raised are two different things.

You need to have complex logic to free up memory which is not needed by your program. Worse, if any other running program or library is leaking the memory then you have no control on it

Only good thing you can do it to shutdown gracefully and that's what most of the programs will chose to do. You can't even decide to wait till memory will be available because it will make your program unresponsive.

\$\endgroup\$
  • \$\begingroup\$ If the games in question literally "do not use exceptions" as the OP said, then they can't catch, raise, or process one. If exceptions are disabled and someone throws one, the program must instead call std::terminate, and that's it. But under such conditions, allocation can return a null pointer instead of throwing an exception, and that can be handled separately. \$\endgroup\$ – underscore_d Aug 8 '16 at 10:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.