When it comes to non memory managed languages such as C, C++ or Fortran (my case), it becomes increasingly difficult to keep track of memory allocation/deallocation, especially when ownership must be shared. I implemented refcounting for my objects, but this makes it even more complicated as there’s no place where an object gets deallocated. Every reference decrement is a potential deallocation point.
So it’s now more and more difficult for me to track down memory leaks. I was wondering about some established techniques to deal with memory management in non-memory managed languages, apart from valgrinding your way around.
Edit: I am more interested in active programming strategies (which are general).
3
You’ve already covered them.
If shared ownership causes memory management issues, work your design away from shared ownership. Usually this involves designating one part of the code as the owner, and thus responsible for deallocation. Weak pointers (or the equivalent) can help this programmatically. RAII can be more liberally applied when single ownership exists.
If you have refcounting (or another mechanism for auto-deallocation) then you need not care if/where the deallocation occurs. You simply need to test/prove that the structure will correctly deallocate memory when it is supposed to.
A few other things to focus on:
- Memory management likes patterns. When your code follows consistent patterns, it will have consistent behavior as far as memory management is concerned. When you do detect memory leaks, you can then focus on the exceptional cases, making them easier to find.
- Shrink your scope. If the objects only live within an isolated scope, it’s easier to reason about ownership and their lifetimes.
- Only allocate what you need. Better than small scopes is no scope. If you don’t need to allocate memory (within reason), don’t.
Tooling is important
There are different tools that may help you. Personally i was using Visual Leak Detector 2 years ago, though it can cause large delays when large blocks are leaked (it displays the contents of the entire leaked block).
If you don’t want to recompile (as Visual Leak Detector requires) I would recommend WinDbg, which is both powerful and fast (though it’s not as easy to use as one could desire).
Here you are some references on memory leaks and memory management in general:
- Memory Leak Detection Using Windbg
- Memory Leak Detection in MFC
- Common WinDbg Commands (Thematically Grouped)
- C/C++ Memory Corruption And Memory Leaks
- The Memory Management Reference
- Using LeakDiag to Debug Unmanaged Memory Leaks
- Heap: Pleasures and Pains
Coding standards and patterns
Beside tooling, i would advice to work on coding standards and patterns
as well as, peer review of the code between developers of the project. This may help to leverage some common approach in trouble shooting memory leak issues.
1
Memory leaks are really a non-issue once you understand how memory management works. To deal with them, you need two things:
First, get a debugging memory manager. I don’t know what’s available for C, C++ or Fortran, since I’m a Delphi developer, but in Delphi there’s a memory manager replacement called FastMM FullDebugMode that will keep track of your allocations and deallocations, and alert you at the end of the program if you’ve leaked anything. It provides a stack trace, type information, and other useful debug information for each leak, which makes them very simple to track down and fix. You ought to have something similar available for other languages.
Second, you need to understand how to structure memory allocation to prevent leaks from happening. The technique for this can be formally stated as the Single Ownership Principle:
Each variable in your code has one and only one owner, and it’s the
responsibility of that owner to free the variable’s memory when it is
no longer needed.
What that single owner is will vary depending on what the variables are and what they’re used for. Primitives, for example, are owned by the stack and will automatically be cleaned up when a function returns. Most objects, by contrast, are owned by another object. Those that aren’t are generally local variables that should be cleaned up by the routine that created them before it exits.
Objects that are shared by multiple other objects do not have multiple owners; if you think of it that way you’re asking for trouble. Instead, they should have one single owner, either a reference-counting system or a master object that you know will be around longer than any of the others.
When you internalize the Single Ownership Principle, you’ll write a lot less code with leaks. And when you have a debugging memory manager to report on whatever you missed, fixing the few mistakes you do make will stop being a scary prospect.
2
I use my own set of wrapper routines for allocation and deallocation, which require each chunk to be identified by a string that identifies the allocation. For example
myAlloc(size,”temporary for mumble sort”)
In debug mode, every chunk is added to a (potentially large) hashtable, and at exit the remaining leaks are reported along with the strings for the trash. In nondebug mode, only summary bookkeeping is done.
From this scheme, I get a tripwire in production code that warns of unexpected leaks, and I get detailed, immediate feedback when developing about what is leaking.