首页 > 解决方案 > 如何从托管 C# 代码跟踪 CRT 调试内存泄漏输出的来源?

问题描述

我正在运行一个用 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# 代码使用。

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

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

标签: visual-c++memory-leakscrtdbg.h

解决方案


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

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

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

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


推荐阅读