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

How to trace the source of a CRT debug memory leak output from managed C# code?

我是 运行 一个用 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")

在我通过 "Stop Debugging" 按钮将其关闭之前,这些消息可能会持续一分多钟。

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

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

如何追踪内存泄漏的源头?有没有办法至少确定 file/project 是什么原因造成的?假设这是来自我们的代码,有没有办法使用 C++/CLI 代码来调试它?

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

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

然而,GC 并没有调用析构函数,而是调用了 finalizer

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