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

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

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

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

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

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

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

这是最后一点信息:我们有一个作为本机控制台应用程序运行的应用程序版本(纯本机 - 根本不是 CLR)。除了没有 UI 内容之外,这与混合模式 95% 相同。这似乎根本不会泄漏内存,并且峰值约为 5MB 专用字节。

所以基本上我在这里想要表达的是,我不认为任何本机代码正在泄漏内存。

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

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

有人有什么建议吗?

有几件事可能对我有帮助:

  • 是否还有我未跟踪的其他类型的内存分配?
  • 为什么数字相加不符呢?我的 CRT 内存使用量为 5 MB,.NET 内存使用量为 1.5 MB,那么为什么整个应用程序使用 30MB 专用字节呢?这一切都与 .NET 框架相关吗?为什么我在泄漏工具中看不到这些?.NET 框架不会显示为某种分配的内存吗?
  • 还有其他与混合模式应用程序配合良好的泄漏检测工具吗?

谢谢你的帮助

约翰

有帮助吗?

解决方案

好吧我终于找到问题了。

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

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

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

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

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

其他提示

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

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

尼什的 代码项目的文章在这里 关于 C++/CLI 堆栈分配语义的内容相当不错,并深入介绍了如何使用{} 进行模拟。

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

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

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

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

  • 在使用的每种 .NET 类型中,如何 许多对象实例正在 分配?
  • 实例有多大 每种类型?
  • 哪些通知 GC 是否按实际情况提供 通过垃圾回收和什么 你能找到答案吗?
  • GC 何时进行 收集对象实例?

应该比某些商品分析器让您有更好的想法,我注意到它们有时可能会产生误导,具体取决于您的分配 porofile(顺便说一句。注意大对象堆问题,> ~83kb 对象是经过特殊处理的,在这种情况下,我建议摆脱大对象堆:)。

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

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

最近这个 MSDN 杂志, ,文章文档中有很多 perfmon 类型的内存探索(后续 这个旧的).

来自 VS 性能博客, ,他们展示了如何在 Visual Studio 中使用 SOS,这可以很方便地追踪 rouge DLL,相关帖子也很好。

毛尼·斯蒂芬的博客公司, ,他说他是性能团队的成员,但基本上他的帖子 100% 都是与 GC 相关的,所以他可能会写得很好。

里克·拜尔斯 是 CLR 诊断团队的开发人员,他的许多博客好友也是很好的来源,但是,我强烈建议也参考相当新的 开发/诊断论坛. 。他们最近扩大了讨论范围。

代码覆盖率工具追踪 通常可以帮助您了解实际运行的情况。

(具体来说,这些特定的统计数据可能无法让您对困扰您的代码的问题有一个全局的了解,我可以说,最近,我发现(即使使用 .net4beta 二进制文件,来自 这家公司, ,非常好,它能够从其配置文件跟踪中导出本机/托管泄漏,让您回到确切的源代码行(即使经过优化,也非常好(并且有 30 天的试用期))))。

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

试用: 调试诊断.
生成一些内存转储后,它会给您一个关于分配了哪些内存的良好摘要,并且根据找到的 PDB,它可以告诉您谁分配了内存。

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

引用泄漏在 .net 中相当于内存泄漏,您持有对对象的引用,这会阻止该对象被垃圾收集,因此您使用的内存开始增加。

您是否可能错过了一些处理程序,如果您使用 GDI+ 和许多其他 API,则可能会发生这种情况。

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top