如何调试堆损坏错误?

新手上路,请多包涵

我正在 Visual Studio 2008 下调试(本机)多线程 C++ 应用程序。在看似随机的情况下,我收到“Windows 已触发断点…”错误,并指出这可能是由于堆。这些错误不会总是立即使应用程序崩溃,尽管它可能会在不久之后崩溃。

这些错误的最大问题是它们仅在实际发生损坏后才会弹出,这使得它们很难跟踪和调试,尤其是在多线程应用程序上。

  • 什么样的事情会导致这些错误?

  • 我该如何调试它们?

欢迎使用提示、工具、方法、启示……。

原文由 Beef 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.4k
2 个回答

应用程序验证器Windows 调试工具 相结合是一个了不起的设置。您可以将两者作为 Windows Driver Kit 的一部分或较轻的 Windows SDK 获得。 (在研究 有关堆损坏问题的早期问题 时发现了 Application Verifier。)我过去也使用过 BoundsChecker 和 Insure++(在其他答案中提到),尽管我很惊讶 Application Verifier 中有多少功能。

Electric Fence(又名“efence”)、 dmallocvalgrind 等都值得一提,但其中大多数在 *nix 下比在 Windows 下运行要容易得多。 Valgrind 非常灵活:我使用它调试过存在许多堆问题的大型服务器软件。

当所有其他方法都失败时,您可以提供自己的全局运算符 new/delete 和 malloc/calloc/realloc 重载——如何这样做会因编译器和平台而异——这将是一项投资——但从长远来看,它可能会有所回报。 dmalloc 和electricfence 以及令人惊讶的优秀书籍 Writing Solid Code 中的理想功能列表应该看起来很熟悉:

  • 哨兵值:在每个分配之前和之后允许更多的空间,尊重最大对齐要求;填充幻数(有助于捕获缓冲区溢出和下溢,以及偶尔的“野生”指针)
  • alloc fill :用一个神奇的非 0 值填充新分配——Visual C++ 已经在调试版本中为您执行此操作(有助于捕获未初始化变量的使用)
  • free fill :用一个神奇的非 0 值填充已释放的内存,旨在在大多数情况下取消引用时触发段错误(有助于捕获悬空指针)
  • 延迟释放:暂时不要将释放的内存返回到堆中,保持空闲填充但不可用(有助于捕获更多悬空指针,捕获接近的双释放)
  • 跟踪:能够记录分配的位置有时很有用

请注意,在我们的本地自制系统(对于嵌入式目标)中,我们将跟踪与大多数其他内容分开,因为运行时开销要高得多。


如果您对重载这些分配函数/运算符的更多原因感兴趣,请查看 我对“任何理由重载全局运算符 new 和 delete?”的回答。 ;除了无耻的自我推销之外,它还列出了有助于跟踪堆损坏错误的其他技术,以及其他适用的工具。


因为在搜索 MS 使用的 alloc/free/fence 值时,我一直在这里找到自己的答案,所以这是 另一个涵盖 Microsoft dbgheap fill values 的答案。

原文由 leander 发布,翻译遵循 CC BY-SA 4.0 许可协议

我想补充一下我的经验。在过去的几天里,我在我的应用程序中解决了这个错误的一个实例。在我的特殊情况下,代码中的错误是:

  • 在迭代它时从 STL 集合中删除元素(我相信 Visual Studio 中有调试标志来捕获这些东西;我在代码审查期间发现了它)
  • 这个比较复杂,我会分步进行:
    • 从本机 C++ 线程回调托管代码
    • 在托管土地上,调用 Control.Invoke 并处置一个托管对象,该对象包装回调所属的本机对象。
    • 由于对象在本机线程中仍然存在(它将在回调调用中保持阻塞,直到 Control.Invoke 结束)。我应该澄清我使用 boost::thread ,所以我使用成员函数作为线程函数。
    • 解决方案:使用 Control.BeginInvoke (我的 GUI 是用 Winforms 制作的),这样原生线程可以在对象被销毁之前结束(回调的目的是准确地通知线程结束并且可以销毁对象)。

原文由 dario_ramos 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题