如何从托管 C# 代码跟踪 CRT 调试内存泄漏输出的来源?

Posted

技术标签:

【中文标题】如何从托管 C# 代码跟踪 CRT 调试内存泄漏输出的来源?【英文标题】:How to trace the source of a CRT debug memory leak output from managed C# code? 【发布时间】:2020-01-22 14:44:14 【问题描述】:

我正在运行一个用 C# (WPF) 编写的软件,该软件使用了大量本机遗留代码。当我关闭软件时,调试器会继续运行,并且我的调试输出屏幕会打印以下内容:

Detected memory leaks!
Dumping objects ->
41198 normal block at 0x00000211F11C58F0, 16 bytes long.
 Data: <                > D8 CE DF B0 8D 00 00 00 00 00 00 00 00 00 00 00 
41194 normal block at 0x00000211C66AD710, 96 bytes long.
 Data: <D:\mydir\somedir> 44 3A 5C 72 65 70 6F 5C 69 71 73 2D 74 72 75 6E 
41193 normal block at 0x00000211F11C5210, 16 bytes long.
 Data: <                > 80 83 A1 E1 11 02 00 00 00 00 00 00 00 00 00 00 
41192 normal block at 0x00000211E1A18360, 88 bytes long.
 Data: <                > 90 80 9D E0 11 02 00 00 90 80 9D E0 11 02 00 00 
(Repeated)

(我将那里显示的路径更改为“mydir\somedir”)

这些消息可能会持续超过一分钟,直到我通过“停止调试”按钮将其关闭。

该软件使用了许多用 C 和 C++ 编写的库。有几个 C++/CLI 项目作为包装器执行并由 C# 代码使用。

我确实可以访问正在使用的本机源代码,因此我搜索了_CRTDBG_MAP_ALLOC 的所有定义并重新定义了new 运算符as explained in MSDN,但输出保持不变并且没有显示行/文件信息。我什至不确定它是否来自我们的代码。

如何追踪此内存泄漏的来源?有没有办法至少确定是什么文件/项目导致了这种情况?假设这是来自我们的代码,有没有办法使用 C++/CLI 代码来调试它?

【问题讨论】:

如果你有所有的源代码,那么是的,但这是一个非常高级的调试。由于每种情况都不同,因此无法解释如何。这些知识来自经验,没有其他方法可以获取它 我已投票结束,因为您在这里没有提出任何具体问题,也没有尝试自行解决等等 代码是谁编写的,他们是否在另一个国家,甚至他们是生是死都无关紧要。代码是C++,称职的C++程序员可以在短时间内发现并识别出这些类型的错误,即使他们自己没有编写代码。您拥有完整的源代码,因此更可以肯定 C++ 程序员可以发现该错误。 问题是调试的话题很长。但是,您确实拥有所有源代码。所以我不知道该告诉你什么,除了花时间学习如何以非 C++ 人员的身份调试 C++ 问题(IMO 不容易),或者雇佣/获得/劫持一个(优秀的)C++ 程序员一段时间确定问题的时间。问题是内存泄漏之一,可能有很多原因 - 一个错误,或者它可能是“合法的”(即实体是全局的,预计会在程序关闭时泄漏)等. 了解直接调试 C++ 代码是目前继续进行的唯一方法,并且无法追踪源头(就正在导入的项目而言,不具体内存分配)没有它。我不明白这个问题和这个答案有什么不合理的地方。如果我在两周前看到它,我可能会为自己节省一些时间,它可以帮助未来的人们。这不是“有可能 X”问题的重点吗?有时答案是简单的“不”。 【参考方案1】:

问题实际上出在 C++/CLI 包装器中。

所有负责释放内存的代码都写在类的析构函数中,假设它们将被垃圾收集器自动调用。

然而,事实证明 GC 并没有调用析构函数,而是调用了finalizer。

解决方案是将所有代码从析构函数移动到终结器,然后当我关闭程序时调试输出中没有显示任何转储行,并且当 GC 运行并且该类被视为已死时释放内存。

【讨论】:

以上是关于如何从托管 C# 代码跟踪 CRT 调试内存泄漏输出的来源?的主要内容,如果未能解决你的问题,请参考以下文章

如何用VS工具检测内存泄露

C++内存泄漏检测工具

如何附加调试器以从托管(C#)包装器进入本机(C++)代码?

在 Spark 1.6.0 中调试“检测到托管内存泄漏”

几百次运行后 fopen() 内存泄漏

PerfView专题 (第二篇):如何寻找 C# 中的 Heap堆内存泄漏