I have some code which has memory leaks as it is getting cycling references among its shared_ptr
instances (this is where two shared_ptr
instances point to objects which each have an internal shared_ptr
reference to the other class instance. This means neither class will ever get destroyed, as each class instance is still in use by the other one, causing a memory leak. In some cases it is a single shared_ptr
instance of a class that references itself, also.)
我有一些代码有内存泄漏,因为它在其shared_ptr实例之间获取循环引用(这是两个shared_ptr实例指向对象,每个对象都有一个内部shared_ptr引用到另一个类实例。这意味着这两个类都不会被破坏,因为每个类实例仍然被另一个类实例使用,导致内存泄漏。在某些情况下,它也是一个引用自身的类的shared_ptr实例。)
Running the code through Valgrind is helpful as it tells me where the memory was originally allocated, however this is not where the cyclic reference originates. I need to find all the places that a specific shared pointer (the one Valgrind complains about) has had its reference count incremented, as one of those will have to be changed to a weak_ptr
to solve the problem.
通过Valgrind运行代码很有帮助,因为它告诉我最初分配内存的位置,但这不是循环引用的来源。我需要找到特定共享指针(Valgrind抱怨的那个)的引用计数递增的所有位置,因为其中一个必须更改为weak_ptr来解决问题。
How can I select a specific shared_ptr
and get a list of all source lines where its reference count was incremented?
如何选择特定的shared_ptr并获取其引用计数递增的所有源行的列表?
I'm running under Linux with GCC/GDB and Valgrind, but platform-neutral solutions would be welcomed.
我在Linux下使用GCC / GDB和Valgrind运行,但平台中立的解决方案将受到欢迎。
Here is some sample code to demonstrate the problem:
以下是一些示例代码来演示此问题:
#include <boost/shared_ptr.hpp>
struct Base {
int i;
};
struct A: public Base {
int a;
boost::shared_ptr<Base> ptrInA;
};
struct B: public Base {
int b;
boost::shared_ptr<Base> ptrInB;
};
int main(void)
{
boost::shared_ptr<A> a(new A); // Line 17
boost::shared_ptr<B> b(new B);
a->ptrInA = b; // Line 19
b->ptrInB = a;
return 0;
}
When run under Valgrind, it says:
在Valgrind下运行时,它说:
HEAP SUMMARY:
in use at exit: 96 bytes in 4 blocks
total heap usage: 4 allocs, 0 frees, 96 bytes allocated
96 (24 direct, 72 indirect) bytes in 1 blocks are definitely lost in loss record 4 of 4
at 0x4C2A4F0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x40099A: main (test.cpp:17)
LEAK SUMMARY:
definitely lost: 24 bytes in 1 blocks
indirectly lost: 72 bytes in 3 blocks
I'm looking for a solution that would point me to lines 19-20 in the source file as possible causes of the cycle, so I can examine the code and make a decision about whether it needs to be changed.
我正在寻找一个解决方案,它将指向源文件中的第19-20行作为循环的可能原因,因此我可以检查代码并决定是否需要更改它。
2 个解决方案
#1
2
You have a design error. And as such, you need to use design debugging tools.
您有设计错误。因此,您需要使用设计调试工具。
Get a Pen and a piece of Paper.
Draw a rectangle for each class type. Draw an arrow from every class that holds a shared_ptr to the class it holds.
If you find a circle, that's your problem.
拿一支笔和一张纸。为每个类类型绘制一个矩形。从包含shared_ptr的每个类中绘制一个箭头到它所拥有的类。如果你找到一个圆圈,那就是你的问题。
Now, each arrow, or link, is created somewhere via shared_ptr assignment.
Look at the suspicious arrows, those that close the circle, and see if they are released properly.
现在,每个箭头或链接都是通过shared_ptr赋值在某处创建的。看看可疑的箭头,那些关闭圆圈的箭头,看看它们是否被正确释放。
#2
1
While Yochai Timmer's approach is fine for smaller projects, I recently had to work on a rather large codebase that used shared_ptr
, the Boost variant, all over the place. This was on Windows and the UI was done on top of MFC, with the main CWinApp
-derived application class pointed to by a shared_ptr
. And it was never getting destructed, resulting in a whole slew of destructors not getting called and some nasty behavior as a result.
虽然Yochai Timmer的方法对于较小的项目来说很好,但我最近不得不在一个相当大的代码库上工作,该代码库使用了整个地方的shared_ptr,Boost变体。这是在Windows上,UI是在MFC之上完成的,主要的CWinApp派生的应用程序类由shared_ptr指向。并且它永远不会被破坏,导致一大堆析构函数没有被调用,结果导致一些令人讨厌的行为。
After trying a variety of leak detectors, I solved the problem by getting the debugger to break on the first line where the offending shared_ptr
was accessed, then searching the relevant headers until I found exactly where the reference counter was. I then added a memory breakpoint to the address of the reference counter and had the VS debugger break on every increment/decrement until the value of the ref ctr failed to drop back to its 'normal' value.
在尝试了各种泄漏检测器之后,我通过让调试器在访问有问题的shared_ptr的第一行中断,然后搜索相关的头文件直到找到引用计数器的确切位置来解决问题。然后,我向参考计数器的地址添加了一个内存断点,并在每次递增/递减时使VS调试器中断,直到ref ctr的值无法回退到其“正常”值。
In my case I knew this shared_ptr
should not have a ref count > 2 after the application's initialization was completed. When it hit 3 and never dropped back to 2 again, I knew I'd found the leak. And the memory breakpoint only needed to be hit about 1000 times or so...
在我的情况下,我知道在应用程序的初始化完成后,shared_ptr不应该具有> 2的引用计数。当它达到3并且再也没有回到2时,我知道我发现了泄漏。内存断点只需要大约1000次左右......
Yes, I'm sure there are better ways of tracking down memory leaks involving shared_ptr
but if all else fails, there's always the brute force approach of watching the reference counter. The details will, of course, depend on your shared_ptr
implementation and how your application is organized.
是的,我确信有更好的方法来跟踪涉及shared_ptr的内存泄漏,但是如果所有其他方法都失败了,那么总是会有强力方法来查看引用计数器。当然,细节取决于您的shared_ptr实现以及您的应用程序的组织方式。
#1
2
You have a design error. And as such, you need to use design debugging tools.
您有设计错误。因此,您需要使用设计调试工具。
Get a Pen and a piece of Paper.
Draw a rectangle for each class type. Draw an arrow from every class that holds a shared_ptr to the class it holds.
If you find a circle, that's your problem.
拿一支笔和一张纸。为每个类类型绘制一个矩形。从包含shared_ptr的每个类中绘制一个箭头到它所拥有的类。如果你找到一个圆圈,那就是你的问题。
Now, each arrow, or link, is created somewhere via shared_ptr assignment.
Look at the suspicious arrows, those that close the circle, and see if they are released properly.
现在,每个箭头或链接都是通过shared_ptr赋值在某处创建的。看看可疑的箭头,那些关闭圆圈的箭头,看看它们是否被正确释放。
#2
1
While Yochai Timmer's approach is fine for smaller projects, I recently had to work on a rather large codebase that used shared_ptr
, the Boost variant, all over the place. This was on Windows and the UI was done on top of MFC, with the main CWinApp
-derived application class pointed to by a shared_ptr
. And it was never getting destructed, resulting in a whole slew of destructors not getting called and some nasty behavior as a result.
虽然Yochai Timmer的方法对于较小的项目来说很好,但我最近不得不在一个相当大的代码库上工作,该代码库使用了整个地方的shared_ptr,Boost变体。这是在Windows上,UI是在MFC之上完成的,主要的CWinApp派生的应用程序类由shared_ptr指向。并且它永远不会被破坏,导致一大堆析构函数没有被调用,结果导致一些令人讨厌的行为。
After trying a variety of leak detectors, I solved the problem by getting the debugger to break on the first line where the offending shared_ptr
was accessed, then searching the relevant headers until I found exactly where the reference counter was. I then added a memory breakpoint to the address of the reference counter and had the VS debugger break on every increment/decrement until the value of the ref ctr failed to drop back to its 'normal' value.
在尝试了各种泄漏检测器之后,我通过让调试器在访问有问题的shared_ptr的第一行中断,然后搜索相关的头文件直到找到引用计数器的确切位置来解决问题。然后,我向参考计数器的地址添加了一个内存断点,并在每次递增/递减时使VS调试器中断,直到ref ctr的值无法回退到其“正常”值。
In my case I knew this shared_ptr
should not have a ref count > 2 after the application's initialization was completed. When it hit 3 and never dropped back to 2 again, I knew I'd found the leak. And the memory breakpoint only needed to be hit about 1000 times or so...
在我的情况下,我知道在应用程序的初始化完成后,shared_ptr不应该具有> 2的引用计数。当它达到3并且再也没有回到2时,我知道我发现了泄漏。内存断点只需要大约1000次左右......
Yes, I'm sure there are better ways of tracking down memory leaks involving shared_ptr
but if all else fails, there's always the brute force approach of watching the reference counter. The details will, of course, depend on your shared_ptr
implementation and how your application is organized.
是的,我确信有更好的方法来跟踪涉及shared_ptr的内存泄漏,但是如果所有其他方法都失败了,那么总是会有强力方法来查看引用计数器。当然,细节取决于您的shared_ptr实现以及您的应用程序的组织方式。