Is my fear of memory leaks justified?
Short answer is yes. Long answer is yes, but there are techniques of reducing them and generally making it easier. The most important of these things, in my opnion, is not to use new/delete lightly and design your program to reduce or elliminate as much dynamic allocation as you can. Instead of allocating memory, try the following and only allocate your own memory when non of these methods work for you (roughly in order of what you should prefer):
- Allocate on the stack
- Use standard containers (C++ Standard Library and/or Boost), eg instead of writing your own linked list, use std::list
- Related to the above, store objects by value if you can (ie, if copy construction isn't too expensive), or at least references (with references you do not need to worry about null) to stack allocated objects
- Pass references to stack objects where possible
- When you do need to allocate your own memory, try to use RAII to allocate in the constructor and free it again in the destructor. Make sure this object is allocated on the stack.
- When you need to use pointers to manually allocated data, use smart pointers to automatically free objects that are no longer used
- Clearly define what objects own what memory. Try to limit your class hierarchy to as few resource owners as possible and let them deal with allocating and releasing objects for you
- If you need more general dynamic memory access, write a resource manager which takes ownership of objects. A handle system as described here is also useful because it allows you to "garbage collect" memory that is no longer needed. Handles could also be tagged with what subsystem owns what memory, so you can dump the state of the systems memory usage, eg, "subsystem A owns 32% of allocated memory"
- Overload operators new and delete for your allocated classes so that you can maintain additional metadata about who/what/where/when allocated what
How can one find out where a memory
leak lies?
Valgrind's suite of tools: Memcheck, Cachegrind, Callgrind, Massif, Helgrind...
You could also try compiling with electric fence (-lefence for gcc) or your compilers equivelent. You can also try Intels suite of tools, especially if you are writing multithreaded code or performance sensitive code (eg, Parallel Studio), though they are expensive.
Aren't there good tools which help in
finding the source of memory leaks
today?
Sure there are. See above.
Now, since you are writing a game, I will share some of my own game development related thoughts/experiences:
- Preallocate as much as you can (ideally everything), at the start of each level. Then during a level, you do not need to worry about memory leaks. Keep pointers to everything you allocated and at the start of the next level, free it all, since there should be no way for dangling pointers to exist when the next level loads its own clean set of data.
- Use stack allocators (either using the actual stack or creating your own in the heap) to manage allocations within a level or frame. When you need memory, pop it off the top of the stack. Then when that level or frame is completed, you can simply clear the stack (if you store only POD types, this is fast and simple: just reset the stack pointer. I use this to allocate messages or my messaging system in my own game engine: during a frame, messages (which are fixed size POD types in my engine) are allocated off a stack-like memory pool (whos memory is preallocated). All events are simply taken from the stack. At the end of the frame, I "swap" the stack for a second one (so that event handlers can also send events) and call each handler on the events. Finally, I simply reset the pointer. This makes allocation of messages very fast and impossible to leak memory.
- Use a resource system (with handles) for all resources which you can't manage through memory pools or stack allocators: buffers, level data, images, audio. My resource system, for example, also supports streaming resources in the background. Handles are created immediately but marked as "not ready" until the resource has finihed streaming.
- Design your data, not your code (ie, design the data structures first). Isolate memory allocation where possible.
- Try to keep similar data together. Not only will this make managing its lifetime easier, but may also improve your engines performance due to better cache utilization (eg, all positions of characters are stored in a container of positions, all positions are updated/processed together, etc).
- Finally, if you can program in as pure a functional style as possible, rather than relying on OOP too much, you can simplify a number of problems: memory management is easier because what parts of your code can mutate data is limited. Allocation happens before the functions are called, deallocation when they are finished (a dataflow pipeline of function calls). Secondly, if you deal with pure-functional code working on immutable data, multicore programming will be greatly simplified. Double win. Eg, in my engine, game objects' data is processed by purely functional code which takes, as input, the current game state and returns, as output, the next frames game state. This makes it very easy to trace which parts of the code can allocate or free memory and generally trace the lifetime of objects. I can also process the game objects in parallel because of this.
Hope that helped.