10

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

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

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

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

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

该工具(内存验证器)设置为跟踪所有内存使用情况(包括 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 框架不会显示为某种分配的内存吗?
  • 任何其他与混合模式应用程序配合良好的泄漏检测工具?

谢谢你的帮助

约翰

4

5 回答 5

7

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

这是由 /EH(异常处理)的错误设置引起的。

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

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

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

就我而言,这只发生在一个罕见的地方,因此我花了很长时间才发现。

于 2010-01-10T11:40:51.303 回答
4

就像 Spence 所说,但对于 C++/CLI ;)....

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

Nish 在代码项目中关于 C++/CLI 堆栈分配语义的文章非常好,并且深入探讨了如何使用{} 进行模拟。

您还应该确保删除任何实现 IDisposable 的对象,因为您不能在 C++/CLI 中调用 Dispose,如果您不使用堆栈语义,删除会为您执行此操作。

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

您可能还想查看这篇关于内存问题的文章,特别是关于事件订阅者的文章,如果您将事件分配给您的对象,您可能会泄漏......

作为最后的手段(或者可能是第一个:),我过去做过的一件事是使用 CLR 分析器 API,这是另一篇关于如何做到这一点的文章,作者的作者 (Jay Hilyard) 有一个例子可以回答;

  • 在使用的每种 .NET 类型中,分配了多少对象实例?
  • 每种类型的实例有多大?
  • GC 在进行垃圾回收时会提供哪些通知,您能发现什么?
  • GC 什么时候收集对象实例?

应该比某些商品分析器更好地了解您,我注意到它们有时会根据您的分配 porofile 产生误导(顺便说一句。注意大型对象堆问题,> ~83kb 对象经过特殊处理,在这种情况下,我'd reccomend,离开大对象堆:)。

鉴于您的评论,还有一些事情......

我之前发布过关于图像加载不收费配额或任何其他可分辨的统计信息,这意味着,您可能需要追踪一些句柄或加载程序问题(最终参见加载程序锁定),但在此之前,您可以尝试设置一些约束Execution Regions,它们可以创造奇迹,但不幸的是,它们也很难改造到非纯代码中。

这个最近的MSDN Mag文章文档中有很多 perfmon 类型的内存 sperlunking(这个旧版本的后续)。

VS Perf 博客,他们展示了如何在 Visual Studio 中使用 SOS,可以很方便地追踪 rouge DLL,相关帖子也不错。

Maoni Stephen 的博客公司,他说他是 perf 团队的一员,但基本上他的帖子 100% 都是关于 GC 的,所以他可能会写它。

Rick Byers是 CLR 诊断团队的开发人员,他的许多博客好友也是很好的来源,但是,我强烈建议您也参考全新的开发/诊断论坛。他们最近扩大了讨论范围。

代码覆盖率工具跟踪通常可以帮助您大致了解实际运行的内容。

(具体来说,那些特定的统计数据可能无法让您全面了解困扰您的代码的原因,我可以说最近,我发现(即使使用 .net4beta 二进制文件,来自该公司的分析器,也非常好,它有能力从它的配置文件跟踪中派生本机/托管泄漏,让您回到确切的源代码行(即使经过优化,也非常好(并且它有 30 天的试用期))))。

祝你好运!!希望其中一些有所帮助,这只是我的记忆,因为我现在正在做很多相同的工作;)

于 2009-07-15T12:35:54.747 回答
0

试用:DebugDiags
在生成一些内存转储后,它会给你一个很好的总结内存分配,并且根据找到你的 PDB,它可以告诉你它是由谁分配的。

于 2009-07-09T07:54:09.123 回答
0

您可能有参考泄漏,请查看 ANTS 分析软件。蚂蚁探查器

引用泄漏是内存泄漏的 .net 等价物,您持有对阻止它被垃圾收集的对象的引用,因此您使用的内存开始增加。

于 2009-07-09T08:27:23.393 回答
0

如果您使用 GDI+ 和许多其他 API,您是否可能错过了一些处理程序。

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

于 2009-07-09T10:26:58.363 回答