I'm programming a game engine in C++, which also has Lua support.
My biggest horror: Memory leaks.
It's not like my game is already infested with them, I'm rather afraid of them popping out of the ground like mushrooms, when the development is in a late phase and the project huge and complex.
I'm afraid of them because they seem extremely hard to spot for me. Especially in sophisticated systems. If my engine is almost finished, the game runs and the memory gets eaten away, what 开发者_如何学JAVAwill I do? Where will I start searching?
- Is my fear of memory leaks justified?
- How can one find out where a memory leak lies?
- Aren't there good tools which help in finding the source of memory leaks today?
How can one find out where a memory leak lies?
Valgrind
Raw pointers are only one potential cause of memory leaks.
Even if you use smart pointers, like shared_ptr, you can get a leak if you have a cycle - the cure for this is to use a weak_ptr somewhere to break the cycle. Using smart pointers is not a cure-all for memory leaks.
You can also forget a virtual destructor in a base class, and get leaks that way.
Even if there are no problems with new-ed objects not being deleted, a long-running process can grow (and appear to leak) because of address space fragmentation.
Tools like valgrind are very, very useful for finding leaks, but they won't always tell you where the fix should be (e.g. in the case of cycles or objects holding onto smart pointers)
A well defined "object lifetime" model is needed. Anytime you do a "new", you need to think about
Who owns this heap object? i.e. who is responsible for maintaining this pointer and allowing other "clients" to reference it?
Who is responsible for deleting this object? This is normally #1, but not necessarily.
Are there any clients of this object whose lifetime is longer than that of this object? If that's true, and they are actually STORING this heap pointer, it will dereference memory that no longer "exists". You may need to add some notification mechanisms or redesign your "object lifetime" model.
A lot of times you fix memory leaks, but then run into problem #3. That's why it's best to have thought out your object lifetime model before you write too much code.
You fight memory leaks by never using raw pointers. If you have code using raw pointers, refactor.
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.
AFAIK Valgrid is only Linux.
For Windows you have tools like BoundsChecker and Purify.
If you're using Visual Studio, the C Runtime library (CRT) also provides a surprisingly simple and useful tool for finding memory leaks out of the box. Read about _CrtDumpMemoryLeaks and its related functions and macros.
It basically allows you to get an indexed dump of the memory leaks when the process exits and then allows you to set a breakpoint in the time where the leaking memory was allocated to see exactly when it happened. This is in contrast to most other tools that only give you post-mortem analysis without a way to reproduce the events that led to the memory leak.
Using these little gems from day one gives you a relative peace of mind that you're in good shape.
Is my fear of memory leaks justified?
If you are writing code that contains them, then absolutely.
How can one find out where a memory leak lies?
By analyzing the code.
Aren't there good tools which help in finding the source of memory leaks today?
Yes, but it's still not easy.
The good news is, it's completely unnecessary with proper design. An ounce of prevention is worth a ton of cure: the best strategy for dealing with memory leaks is to write the code in a way that guarantees that there aren't any.
At the risk of sounding like the smug jerk that I probably am, consider using any programming language developed after 1979, which don't have problems with memory leaks, heap corruption stack corruption, or even memory management. (Anyone who says something like "I need my program to be fast" has probably never heard of Donald Knuth.)
Memory leaks are not too scary - but they can be detrimental to a program's performance (so get rid of them!).
- Don't be afraid of memory leaks. Think about what they are - memory which hasn't been deleted but to which all access is removed (so until execution ends, the system doesn't know it can re-allocate that memory).
- You can find memory leaks "by-hand" so to speak by going through your objects and being sure they're deleted in the proper place (and only once! otherwise other errors will come about). Tools such as Valgrind can help spot where the error may be.
- As someone mentioned before (and I mentioned above) Valgrind is a great tool to finding memory leaks. Running it like this:
valgrind --leak-check=full -v ./YOUR_EXECUTABLE
That will give you a full leak check and verbose (-v) output on how memory is being used in your program.
Regards,
Dennis M.
At least for the Lua part you can use your own memory allocator and track all allocations and freeing and so spot any memory leaks.
There are various techniques to track memory leaks.
The simplest is to use a macro and a specific allocator that would store the function that allocated this. That way, you could track each allocation and see which aren't deleted when they should be. Then you can start writing unittest and assert that memory has been freed.
If you use pre-compiled containers all the time, this won't work as all allocations will be in containers. Then your options are:
- Use a thread-local-value to identify the subsystem or class id (in debug builds) that's running, so that your allocator can detect who is allocating memory. You could even use a stack to track memory usage hierarchially in your engine.
- Actually retrieve the call stack and store that, if your compiler has sufficient support.
- Use memory pools for the subsystems, and measure if their size increases disproportionately. (This is also a (admittedly poor) workaround for leaky memory, since you could free the entire pool at once, thus freeing leaked memory too, if you're able.)
- On Windows, there are some macros that track memory allocation by source line automatically under debug builds.
There are probably more options than that. Testing and the use of a custom global new/delete override (that can be queried) should prove useful, if your design permits it.
Also, see the Electronic Arts STL C++ paper for some discussion on what needs to be done in STL/C++ to support proper game development. (It's probably a bit more hardcore than your engine, but it certainly contains many nuggets of inspiration and ingenuity.)
How can one find out where a memory leak lies?
Visual Leak Detector for Visual C++ 2008/2010
精彩评论