查找shared_ptr的引用计数递增的位置

时间:2022-09-05 18:43:05

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实现以及您的应用程序的组织方式。