【问题标题】:Is there a Valgrind for detecting "Memory Leaks" with Smart Pointers是否有使用智能指针检测“内存泄漏”的 Valgrind
【发布时间】:2016-10-10 16:26:43
【问题描述】:

我们的团队最近开始试验智能指针,我越来越担心我们检测和纠正内存泄漏的能力。

我理解(我认为......)在最纯粹的意义上,智能指针很少有真正的泄漏,如果有的话,它通常与循环引用有关。但是,恐怕这掩盖了“不需要和不必要的内存消耗”意义上的内存泄漏问题。

例如,当使用传统的原始指针时,如果一个对象无法正确清理内存,我们可以运行 Valgrind,然后在程序结束时,剩下的就是一个泄漏(通常这可能是一个继续增长的泄漏在程序执行期间导致严重问题)。 . .现在有了智能指针,这些都在程序退出之前被清理干净,Valgrind 不会看到任何丢失的对分配内存的引用。

总的来说,我看到智能指针在帮助开发人员防止泄漏方面的价值,但是我们是人类并且会犯错误,这就是我们有调试工具的原因!

我们如何检测由于编程错误导致的不必要的内存增长,就像我们过去能够使用 Valgrind 和原始指针一样?

...编辑

1) 愚蠢的是,我通常将 Valgrind 与 Memcheck 联系起来,这就是我打算在上面使用它的方式。我为混乱道歉。

2) 作为一个例子,这里是一个例子:

考虑使用多线程应用程序来处理数据。第一个线程读取输入(从磁盘或网络中),然后通过队列将数据块传递给另一个运行算法步骤 1 的线程,步骤 1 的线程从其输入队列中抓取数据,然后将数据传递给步骤 2。重复直到最后一个线程写出结果(到磁盘或网络)。处理完所有输入数据后,每个线程在完成最后一项并退出程序后都会杀死自己。

现在在执行过程中,我们的足迹显着增长,假设我们可以像摄取数据一样快地处理数据,那么肯定存在泄漏。我使用原始指针来检测这种 Memcheck 错误,它会在程序结束时报告丢失或可能丢失的内存块,我们可以追踪和修复。

3) 在我们匆忙学习和应用智能指针时,我们可能使用了“shared_ptr”,因为另一种类型可能更适合我们的应用程序。

【问题讨论】:

  • 我不明白为什么 valgrind 不起作用。如果您有泄漏,则意味着资源不会被自动清理并且应该能够被检测到。
  • 因为当智能指针失去作用域时,它会自动销毁自己。当创建它的对象失去作用域时,就会发生这种情况。如果一个对象持续整个运行时(比如一个代表一个线程及其要处理的数据的输入/输出队列)并且由于错误而导致内存消耗增加,智能指针仍然在程序退出时被销毁,而 valgrind 不会注意损失。
  • 我不确定智能指针 API 的设置方式是否会发生这种情况。如果你有一个很好看的例子。
  • 好的工具不能代替好的工艺。您仍然必须编写合理的代码,这意味着拥有最少的所有权(始终是唯一的,从不共享,除非您更了解)、严格的范围和代码审查。智能指针你写出好的代码;它们不会神奇地使任意代码变好。
  • 写出好的代码始终是我们的目标,但错误总会发生,这就是为什么我们有工具来帮助我们找出错误!

标签: c++ pointers memory memory-leaks


【解决方案1】:

如果内存显着增长,您将直接看到或使用 valgrind 的 massif 工具。

另外值得注意的是,智能指针有很多种。 你提到循环引用,所以大概你在谈论shared_ptr?这是循环引用存在风险的唯一情况——它并不适用于所有智能指针。

一般来说,按优先顺序排列:

  1. 避免(直接)动态分配 - 使用容器或其他专用类型来管理内存
  2. 避免复杂的所有权和/或动态分配资源的生命周期:unique_ptr 很容易推理
  3. 如果您必须共享所有权,这意味着您的对象生命周期本质上是不确定的,因此难以推理。但是,您不能在不泄露其所有者的情况下泄露“共享”对象,因此泄露意味着您的程序结构更高层次的混乱

    • 请注意,如果您的引用不应使共享对象保持活动状态,则应改用weak_ptr。这也有助于打破静态可预测的循环

【讨论】:

  • 我注意到循环引用是为了清楚和区分我正在寻找的内存泄漏类型。听起来地块可能是要走的路
【解决方案2】:

根据我在c++ char* + std::vector memory leak的回答

内存泄漏有很多定义 - valgrind 不会检测仍在范围内的东西。这是工程师和调试器的工作。 (任何软件如何猜测您何时可能决定要使用仍在范围内的东西?)

【讨论】:

  • 这正是我们过去使用 memcheck 能够解决的问题。根据您的链接答案,也许我们应该开始使用 Massif 来分析堆并手动检测不需要的增长?
  • 这将检测到它 - 但如果您已经知道自己遇到了这个问题,那么您将看不到任何新内容。看看单例和全局变量,它们是一个很好的起点
  • 看起来 Massif 提供了某种程度的跟踪分配发生的位置,这应该等同于 memcheck 的“track-origins”,我们将试一试!
【解决方案3】:

使用智能指针需要注意的一件事是类之间的循环依赖关系,例如彼此的std::shared_ptr<> 将无法正确清理。

恐怕这是 valgrind 无法检测到的情况。

虽然这可以使用std::weak_ptr<> 解决。

【讨论】:

  • 这就是弱引用存在的原因:)
  • @UKMonkey 当然。这是解决这类问题的一种方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-28
  • 1970-01-01
  • 2015-08-12
  • 2012-09-30
  • 1970-01-01
  • 2015-08-10
  • 2010-09-09
相关资源
最近更新 更多