混合模式C ++ / CLR应用程序中的内存泄漏

时间:2009-07-06 21:27:56

标签: .net c++ memory-leaks mixed-mode

我在混合模式C ++ / CLR .NET应用程序中遇到内存泄漏问题。

(它是使用“/ clr”编译器设置链接到VS2008 C ++ / CLR Windows窗体应用程序的C ++本机静态库)

典型行为:应用程序开始使用30 MB(私有内存)。然后泄漏内存减慢,比如在模拟重负载下运行时每小时一MB。这模拟了数天或数周的应用程序。

我尝试使用多种工具来跟踪内存泄漏,包括Visual Studio CRT库附带的CRT调试内容。我还使用了商业泄漏检测工具(“Memory Validator”)。

两者都报告在关机时可忽略不计的内存泄漏(一些小的条目,相当于我不担心的几KB)。此外,我可以看到,在运行时,跟踪的内存似乎没有那么多(所以我不相信它只是被占用的内存,只在app退出时释放)。我得到大约5 MB的列出内存(总计> 30MB)。

设置工具(Memory Validator)以跟踪所有内存使用情况(包括malloc,new,虚拟内存分配和一大堆其他类型的内存分配)。基本上,已选择要跟踪的内存的每个设置。

.NET图像报告它使用了大约1.5 MB的内存(来自perfmon)。

这是最后一点信息:我们有一个版本的应用程序作为本机控制台应用程序运行(纯粹是原生的 - 根本不是CLR)。这与混合模式的95%相同,除了没有UI的东西。这根本不会泄漏内存,并且大约在5MB私有字节处达到峰值。

所以基本上我试图在这里得到的是,我认为任何本机代码都不会泄漏内存。

另一个难题:我发现这是指在定位2.0框架(我是)时混合模式应用程序中的内存泄漏:http://support.microsoft.com/kb/961870

不幸的是,细节非常稀疏,所以我不确定它是否相关。我确实尝试过针对3.5框架而不是2.0,但仍然遇到了同样的问题(也许我没有做到这一点)。

有人有任何建议吗?

可能有助于我的一些事情:

  • 我还没有其他类型的内存分配吗?
  • 为什么这些数字不加起来?我得到5 MB的CRT内存使用量,1.5 MB的.NET内存,那么为什么整个应用程序使用30MB的私有字节呢?这一切都绑在.NET框架中吗?为什么我在泄漏工具中看不到这些? .NET框架不会显示为某种分配的内存吗?
  • 任何其他适用于混合模式应用的泄漏检测工具?

感谢您的帮助

约翰

5 个答案:

答案 0 :(得分:6)

好的,我终于找到了问题。

这是由于/ EH(异常处理)设置不正确引起的。

基本上,对于混合模式.NET应用程序,您需要确保使用/ EHa而不是默认/ EH编译所有静态链接的库。

(应用程序本身也必须使用/ EHa编译,但这是给定的 - 如果不使用它,编译器将报告错误。问题是当你链接到其他静态本机库时。)

问题是,在使用/ EH编译的本机库中抛出的应用程序托管位中捕获的异常最终无法正确处理异常。然后,不会正确调用C ++对象的析构函数。

在我的情况下,这只发生在一个罕见的地方,因此为什么我花了很长时间才发现。

答案 1 :(得分:4)

像Spence说的那样,但是对于C ++ / CLI;)....

对于您在C ++ / CLI中使用的任何对象,如果您从C ++代码创建更多该对象,您应该尝试使用堆栈分配语义,即使这是一个编译器神奇的东西,它能够设置您可能习惯使用本机代码的嵌套__try {} __finally {}语句(设置它们以避免调用Dispose)。

关于C ++ / CLI堆栈分配语义的

Nish's article at the code project here非常好,并深入探讨如何使用{}进行模拟。

你还应该确保删除任何对象的impisment IDisposable,因为你不能在C ++ / CLI中调用Dispose,如果你没有使用堆栈语义,delete会为你做这个..

我通常在Streams上调用Close,并在完成对象时尝试分配nullptr,以防万一。

您可能还想查看this article on memory issues,特别是关于活动订阅者,如果您要将事件分配给您的对象,则可能会泄露......

作为最后的手段(或者可能是第一个:),我过去做的一件事就是利用CLR探查器API,here's another article如何做到这一点,作者的作者(Jay Hilyard)已经一个回答的例子;

  • 在使用的每种.NET类型中,如何使用 许多对象实例正在存在 分配呢?
  • 实例有多大 每种类型?
  • 什么通知 GC是否按原样提供 通过垃圾收集和什么 你可以找到?
  • 什么时候GC 收集对象实例?

应该比一些商品分析器给你一个更好的想法,我注意到它们偶尔可能会误导,具体取决于你的分配porofile(顺便提一下。注意大对象堆问题,>〜83kb对象是专门处理的,在那个例子,我建议你退出大对象堆:)。

鉴于你的意见,还有一些事情......

我之前发布过关于图片加载不收费或任何其他不可取的统计信息,这意味着,您可能需要追踪一些句柄或加载器问题(最终请参见加载程序锁定),但在此之前,您可以尝试设置一些Constrained Execution Regions,它们可以创造奇迹,但遗憾的是难以改造成非纯粹的代码。

这篇最新的MSDN Mag文章文档中有很多perfmon类型的内存消耗(this older one的后续内容)。

VS Perf Blog开始,他们展示了如何在Visual Studio中使用SOS,这可以很方便地追踪胭脂DLL,相关帖子也很好。

Maoni Stephen's Blogcompany,他说他是在执行团队,但基本上100%的帖子都与GC有关,所以他可能写得很好。

Rick Byers是CLR诊断团队的开发人员,他的许多博客伙伴也是很好的来源,但是,我强烈建议也引用相当新的dev/diagnostics forum。他们最近扩大了讨论范围。

Code Coverage Toolstracing通常可以提供帮助,让您了解实际运行的内容。

(特别是那些特定的统计数据可能不会让你全面了解你的代码是什么,我可以说最近,我发现(即使是.net4beta二进制文件,来自this company的分析器),相当不错,它能够从它的轮廓痕迹中获取原生/管理泄漏,带您回到确切的源代码行(即使经过优化,相当不错(并且它有30天试用)))。)

祝你好运!希望其中一些有所帮助,这只是我心中的新鲜事,因为我现在正在做同样的工作;)

答案 2 :(得分:0)

试试:DebugDiags
在生成一些内存转储后,它将为您提供一个很好的总结,分配内存,并根据找到您的PDB,它可以告诉您谁被分配。

答案 3 :(得分:0)

您可能有参考泄漏,请查看ANTS分析软件。 Ants Profiler

引用泄漏是内存泄漏的.net等价物,你保存对一个对象的引用,该对象阻止它被垃圾收集,因此你使用的内存开始上升。

答案 4 :(得分:0)

你是否有可能错过了一些处理器,如果你使用GDI +和许多其他API就会发生。

如果您运行静态分析工具FXCop,它有一个规则来检查您是否在提供接口的对象上调用dispose(或使用“using”)语句。在.Net中,如果函数使用非托管代码,它通常会为您提供一个dispose或close方法,以防止泄漏资源/内存。