Is there any tips/tricks for finding cyclic references of shared_ptr's?
This is an exmaple of what I'm trying to find - unfortunately I can't seem to find the loop in my code.
struct A
{
bo开发者_Python百科ost::shared_ptr<C> anC;
};
struct B
{
boost::shared_ptr<A> anA;
};
struct C
{
boost::shared_ptr<B> anB;
};
I'd recommend using Valgrind. When you shut down the process, it will show you all leaked memory. Unless your shutdown somehow breaks the cycle, any cycles should show up as leaked memory and Valgrind will tell you where in your code the memory was originally allocated from.
I was in charge of the design of a credit risk system once (in C++, though that isn't relevant). These things are really big graphs with risk allocated at the nodes. We had a simple heuristic to find if we were in a cycle - if we traversed more than 500 times (I forget the exact figure - it was configurable), the answer was yes. Most cycle detection schemes rely on heuristics like this.
I've had similar problems in the past - memory leaks due to shared_ptr cyclic references that went undetected for months.
Watch out for "caches". I have an object (let's call it "Factory") which handled out items ("Widget"). Widgets had the property of being A) Immutable, and B) Had a shared_ptr<Factory>
to its creator (it sometimes created other widgets, etc). Everything worked fine, until I added a Widget cache to Factory - since the Widgets were immutable, it made sense to cache them, to hand back the same Widget everytime it was requested. My cache was a cache of shared_ptr<Widget>
, so instant silent leak. The fixes are obvious, so I won't go into them.
Ultimately I was able to lean on the platform I was using to detect such memory leaks from the CRT. Visual Studio's CRT has memory leaked detection and reporting, which I enabled in my test program to prevent regressions:
int main()
{
// code code code
// code code code
#ifdef _MSC_VER
_CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
_CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT );
_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );
_CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT );
_CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
_CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT );
_CrtDumpMemoryLeaks();
#endif
}
GCC probably has similar basic leak reporting, but I don't know what it is.
I used a combination of the above posts. I used a memory profiler, came up with some suspected cycles and broke those by using weak_ptr's.
I've used the built in CRT memory leak detection before, but unfortunately in my case there are several static singletons that dont get deallocated until module unload which I believe is after the CRT detectors lifecycle. Basically it gives alot of spew that are false positives.
I guess the simplest answer is that there is only so much smart pointers can do for you:
I suggest recording whenever you make a loop, (easy if you create all three objects at once, trickier otherwise...), and then check that record where you delete / unlink objects, or just periodically if that is not possible.
You could implement some sort of debugging interface that returns a list of shared_ptrs owned by this object. You'd need to do this for every class stored in a shared_ptr. Now you have a generic graph which you can traverse, and can use cycle detection algorithms on it. I believe Tarjan's strongly connected component algorithm might work for this but graph theory is not my forté.
精彩评论