我有一些代码存在内存泄漏,因为它在其 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 行是循环的可能原因,这样我就可以检查代码并决定是否需要更改它。
最佳答案
基于@dandan78 的方法。这是一个更详细的例子
GDB CLI,它在 shared_ptr
的引用计数更改上创建断点。
主要.cpp:
#include <iostream>
#include <memory>
using namespace std;
#define DBG(msg) std::cout << msg << std::endl;
class A {
public:
A(int i) {
mI = i;
DBG("A() this:"<<this<<" i:"<<mI);
}
~A() {
DBG("~A() this:"<<this<<" i:"<<mI);
}
private:
int mI = 0;
};
int main() {
std::shared_ptr<A> p1(new A(0x12345678));
DBG("p1 use_count:"<<p1.use_count());
{
auto p2 = p1;
DBG("p1 use_count:"<<p1.use_count());
DBG("p2 use_count:"<<p2.use_count());
auto p3 = p1;
DBG("p1 use_count:"<<p1.use_count());
DBG("p2 use_count:"<<p2.use_count());
DBG("p3 use_count:"<<p3.use_count());
}
DBG("p1 use_count:"<<p1.use_count());
return 0;
}
生成文件:
CXXFLAGS = -O0 -ggdb
main: main.cpp
$(CXX) $(CXXFLAGS) -o $@ $<
程序的输出:
A() this:0x6c6fb0 i:305419896
p1 use_count:1
p1 use_count:2
p2 use_count:2
p1 use_count:3
p2 use_count:3
p3 use_count:3
p1 use_count:1
~A() this:0x6c6fb0 i:305419896
编译并运行gdb(不要把#注释粘贴到gdb):
make
gdb main 2>&1 | tee out.log
GDB session :
(gdb) b main.cpp:23 # right after the p1 initialization
(gdb) r
Thread 1 hit Breakpoint 1, main () at main.cpp:23
(gdb) x/2xg &p1
0x62fe00: 0x0000000000fd4a10 0x0000000000fd4a50
# First pointer points to the target A object, sencond points to the reference counter
# Inspect the refcount data:
(gdb) x/4xw 0x0000000000fd4a50
0xfd4a50: 0x00405670 0x00000000 0x00000003 0x00000001
# The third integer is use_count of the shared_ptr, which can be printed by:
(gdb) x/1xw 0x0000000000fd4a50 + 8
0xfd4a58: 0x00000001
# Add a watchpoint for the use_count address
(gdb) watch *(int*)(0x0000000000fd4a50 + 8)
Hardware watchpoint 2: *(int*)(0x0000000000fd4a50 + 8)
# Add commands for the new watchpoint 2:
(gdb) commands 2
bt # backtrace
c # continue
end # end of the handler script
(gdb) c # Continue the program
现在您可以检查 out.log
文件并分析 use_count 发生变化的所有回溯。
也可以直接添加 gdb 观察点:
watch *(*((int**)(&p1) + 1) + 2)
^--------------- the shared_ptr variable
^--------- +1 pointer to the right (+8 bytes in 64bit programm)
^---- +2 integers to the right (+8 bytes)
如果您使用优化进行编译,shared_ptr 变量可能已经被优化掉了。只需在您的代码中直接打印它,然后获取 shared_ptr 对象的地址并将其粘贴到您的 gdb session 中:
std::cout << "p1:" << (void*)&p1 << std::endl;
关于c++ - 查找 shared_ptr 的引用计数增加的位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28291515/