我有一些代码在shared_ptr
示例之间循环引用时发生内存泄漏(这是两个shared_ptr
示例指向各自具有对另一个类示例的内部shared_ptr
引用的对象的情况。这意味着没有一个类将被销毁,因为每个类示例仍由另一个使用,导致内存泄漏。在某些情况下,它也是引用自身的类的单个shared_ptr
示例。)
通过Valgrind运行代码很有帮助,因为它告诉我内存最初分配的位置,但这不是循环引用的来源。我需要找到特定共享指针(Valgrind抱怨的那个)的引用计数增加的所有位置,因为其中一个必须更改为weak_ptr
才能解决问题。
如何选择一个特定的shared_ptr
,并获得其引用计数递增的所有源代码行的列表?
我在Linux下运行GCC/GDB和Valgrind,但是平台中立的解决方案会受到欢迎。
下面是演示该问题的一些示例代码:
#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;
}
在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
我正在寻找一种解决方案,它可以将源文件中的第19-20行作为循环的可能原因,这样我就可以检查代码并决定是否需要更改它。
3条答案
按热度按时间gmol16391#
基于@dandan78的方法,下面是GDB CLI的一个更详细的例子,它在
shared_ptr
的引用计数改变时创建一个断点。main.cpp:
生成文件:
程序输出:
编译并运行gdb(不要将#comments粘贴到gdb):
广发会议:
现在,您可以检查
out.log
文件并分析use_count更改的所有回溯。也可以直接添加gdb观察点:
如果使用优化编译,shared_ptr变量可能已经被优化掉了,只需直接在代码中打印出来,然后获取shared_ptr对象的地址并粘贴到gdb会话中即可:
cyej8jka2#
虽然Yochai Timmer的方法对于较小的项目来说是不错的,但我最近不得不在一个相当大的代码库上工作,该代码库到处都使用了
shared_ptr
,Boost的变体。这是在Windows上进行的,UI是在MFC上完成的,CWinApp
派生的主应用程序类由shared_ptr
指向。而且它从未被析构。从而导致大量析构函数没有被调用,并导致一些令人讨厌的行为。在尝试了各种泄漏检测器之后,我解决了这个问题,方法是让调试器在访问有问题的
shared_ptr
的第一行中断,然后搜索相关的头文件,直到找到引用计数器的确切位置。然后,我在引用计数器的地址上添加了一个内存断点,并让VS调试器在每次增量/递减,直到REF_CTR的值不能降回到其“正常”值。在我的例子中,我知道这个
shared_ptr
在应用程序初始化完成后不应该有一个大于2的引用计数,当它达到3并且再也没有降回2时,我知道我找到了泄漏,并且内存断点只需要被达到大约1000次...是的,我确信有更好的方法来跟踪
shared_ptr
的内存泄漏,但是如果所有方法都失败了,那么总是有监视引用计数器的暴力方法,当然,细节将取决于你的shared_ptr
实现和你的应用程序是如何组织的。ncecgwcz3#
您有一个设计错误。因此,您需要使用设计调试工具。
拿一支笔和一张纸。
为每一个类类型画一个矩形,从每一个拥有shared_ptr的类画一个箭头到它所拥有的类。
如果你找到一个圆,那是你的问题。
现在,每个箭头或链接都通过shared_ptr赋值在某处创建。
看看那些可疑的箭头,那些封闭圆圈的箭头,看看它们是否被正确地释放。