【问题标题】:Design pattern to detect memory leaks for reference counted smart pointers用于检测引用计数智能指针的内存泄漏的设计模式
【发布时间】:2012-09-30 02:22:54
【问题描述】:

我们有自己的智能指针类,它使用基本的 AddRef 和 Release 进行引用计数。

在调试时,我可以看到很多对象没有被正确释放。我可以看到哪些对象没有被释放,但很难找出它们是从哪个位置分配的。

有什么好的设计模式可以解决这个问题吗?

我已经放置了调试语句以及 ADDREF 和 RELEASE,当调用 release 时我可以看到引用计数。在某些情况下它大于 1,这意味着指针没有被删除。

如果我开始在 shared_ptr const 上设置断点,那么它将被调用数百次,并且很难查明内存泄漏。

似乎创建了一些导致这些泄漏的循环引用。

【问题讨论】:

  • 你是如何检测到东西没有被正确发布的?您使用的是哪个 IDE? MSVC 有一个快速而肮脏的_CrtSetBreakAlloc,如果您可以确定地重现问题,这可能会有所帮助。
  • 如果你有循环数据结构,那么不要使用引用计数。没有办法修复引用计数以考虑指针周期。
  • 你是否正确实现了复制构造函数和赋值?
  • 为什么不使用标准智能指针?
  • 如果 RAII 实现正确(首先要检查),如果计数器不是原子管理的,多线程问题可能会破坏引用计数。最好的方法是使用{std[::tr1]|boost}::shared_ptr(或ATL 智能指针,如果您使用它作为实现IUnknown 的一种方式)。但无论如何你可能都知道。

标签: c++ memory-leaks smart-pointers


【解决方案1】:

不要为此使用设计模式。不要仅仅为了检测泄漏而添加代码膨胀。

使用记忆工具。我想到了 Valgrind,以及一些用于 Windows 的商业版本。

还有……

我可以看到哪些对象没有被释放,但很难找出它们是从哪个位置分配的。

为什么?在对象的构造函数中设置断点。或者改用标准指针(std::shared_ptrstd::unique_ptr)。

根据您的编辑:

我已经放置了调试语句以及 ADDREF 和 RELEASE,当调用 release 时我可以看到引用计数。在某些情况下它大于 1,这意味着指针没有被删除。

您是否考虑了复制省略 (RVO/NRVO)?这似乎更有可能是原因。

【讨论】:

  • 您知道,我几乎从不回答您已经回答过的问题,因为我通常 100% 同意您的看法。这次不行。
  • @JohnDibling :) 好吧,在那种情况下,我期待答案(添加后请联系我)
  • 已发布。 OP 的编辑在一定程度上改变了场景,但是,嘿——我花了大约 10 分钟打字。无论如何,你会得到我的 +1。
【解决方案2】:

几年前,在auto_ptr 之前,我编写了自己的智能指针类,我们在代码库中广泛使用了它。当然,它充满了错误。

我遇到了完全相同的问题。虽然我无法向您展示我用来识别引发资源泄漏的调用站点的实际代码(因为代码不再存在),但我可以告诉您我做了什么。

第一件事是我写了一个#define 宏>SHUDDERhere,我可以用它来代替new 来分配我的对象。宏向智能指针发送附加参数,指示调用站点的文件和行号。智能指针会保留这个。

然后我用我的宏替换了全局new,当时我说出了#defines 的神奇咒语来打开此功能。显然,这应该在所有 Releasse 构建中禁用,并且仅在您实际调试 this 时在 Debug 构建中使用。

然后我让我的程序运行,直到我想查看所有呼叫站点。我会在那里的智能指针上执行dump_call_sites() 方法,它将调用站点字符串的static vector 转储到标准输出。因此发现问题,我会恢复所有那些神奇的咒语来关闭所有这些 hokus pokus。

这是真正的 hack-n-slash 编码。它远非优雅,它引入了它自己的一系列问题。但随着printf 调试,它也有它的位置。如果您不想或无法加载您的 Rational Purify 或 Bounds Checker 类型的产品,这可以帮助您快速确定泄漏的来源。

【讨论】:

  • 公平的选择,但我更喜欢使用现有的工具。这似乎为可以使用其他方式识别的东西增加了很多代码膨胀。但我确实倾向于编写自己的工具和机制。 :)
  • 我喜欢它不是因为它是我发明的,而是因为它比加载 Rational Purify 并等待 3 天让我的程序运行要容易得多。
  • 另一种选择是一些调试器脚本。在相关函数中放置断点,附加从调用堆栈(调用函数的文件/行等)中获取相关信息的脚本并将其打印出来。项目中没有代码膨胀,但工作/脚本/断点可以很容易地与项目共享。
  • Purify 确实很痛苦(但如果我没记错的话,它会缓存已检测的二进制文件,因此将其放置一夜可能会完成并修复以后的运行)。
  • @bames53:我想订阅你的时事通讯。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-09
  • 2012-10-01
  • 2015-10-30
  • 1970-01-01
  • 2012-11-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多