Does anybody know a "technique" to discover memory leaks caused by smart pointers? I am currently working on a large project written in C++ that heavily uses smart pointers with reference counting. Obviously we have some memory leaks caused by smart pointers, that are still referenced somewhere in the code, so that their memory does not get free'd. It's very hard to find the line of code with the "needless" reference, that causes the corresponding object not to be free'd (although it's not of use any longer).
I found some advice in the web, that proposed to collect call stacks of the increment/decrement operations of the reference counter. This gives me a good hint, which piece of code has caused the reference counter to get increased or decreased.
But what I need is some kind of algorithm that groups the corresponding "increase/decrease call stacks" together. After removing these pairs of call stacks, I hopefully have (at least) one "increase call stack" left over, that shows me the piece of code with the "needless" reference, that caused the corresponding object not to be freed. Now it will be no big deal to fix the leak!
But has anybody an idea for an "algorithm" that does the grouping?
Development takes place under Windows XP.
(I hope someone understood, what I tried to explain ...)
EDIt: I am talking about leaks caused by circular references.
If I were you I would take the log and write a quick script to do something like the following (mine is in Ruby):
This basically goes through the log and captures each allocation/deallocation and stores a unique value for each pair, then sort it and remove pairs that match, see what's left.
Update: Sorry for all the intermediary edits (I accidentally posted before I was done)
To detect reference cycles you need to have a graph of all reference-counted objects. Such a graph is not easy to construct, but it can be done.
Create a global
set<CRefCounted*>
to register living reference-counted objects. This is easier if you have common AddRef() implementation - just addthis
pointer to the set when object's reference count goes from 0 to 1. Similarly, in Release() remove object from the set when it's reference count goes from 1 to 0.Next, provide some way to get the set of referenced objects from each
CRefCounted*
. It could be avirtual set<CRefCounted*> CRefCounted::get_children()
or whatever suits you. Now you have a way to walk the graph.Finally, implement your favorite algorithm for cycle detection in a directed graph. Start the program, create some cycles and run cycle detector. Enjoy! :)
The way I do it is simply: - on every AddRef() record call-stack, - matching Release() removes it. This way at the end of the program I'm left with AddRefs() without maching Releases. No need to match pairs,
First step could be to know what class is leaking. Once you know it, you can find who is increasing the reference: 1. put a breakpoint on the constructor of class that is wrapped by shared_ptr. 2. step in with debugger inside shared_ptr when its increasing the reference count: look at variable pn->pi_->use_count_ Take the address of that variable by evaluating expression (something like this: &this->pn->pi_.use_count_), you will get an address 3. In visual studio debugger, go to Debug->New Breakpoint->New Data Breakpoint... Enter the address of the variable 4. Run the program. Your program will stop every time when some point in the code is increasing and decreasing the reference counter. Then you need to check if those are matching.
If you can reproduce the leak in a deterministic way, a simple technique I often used is to number all your smart pointers in their order of construction (use a static counter in the constructor), and report this ID together with the leak. Then run the program again, and trigger a DebugBreak() when the smart pointer with the same ID gets constructed.
You should also consider this great tool : http://www.codeproject.com/KB/applications/visualleakdetector.aspx
It's not a matter of finding a leak. In case of smart-pointers it'll most probably direct to some generic place like CreateObject(), which is being called thousands of time. It's a matter of determining what place in the code didnt call Release() on ref-counted object.