我的混合模式 C++/CLR .NET 应用程序遇到缓慢内存泄漏问题。
(它是使用“/clr”编译器设置链接到 VS2008 C++/CLR Windows 窗体应用程序的 C++ 本机静态库)
典型行为:应用程序启动时使用 30 MB(私有内存)。 然后,在模拟重负载下运行时,内存泄漏速度会变慢,例如每小时泄漏一个 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 为目标,但仍然遇到同样的问题(也许我没有做对)。
有人有什么建议吗?
有几件事可能对我有帮助:
- 是否有我没有跟踪的任何其他类型的内存分配?
- 为什么数字相加不符呢? 我使用了 5 MB 的 CRT 内存,1.5 MB 的 .NET 内存,那么为什么整个应用程序使用 30MB 的私有字节呢? 这一切都与 .NET 框架相关吗? 为什么我在泄漏工具中看不到这些? .NET 框架不会显示为某种分配的内存吗?
- 还有其他与混合模式应用程序配合良好的泄漏检测工具吗?
感谢
约翰的任何帮助
I'm having problems with a slow memory leak in my mixed mode C++/CLR .NET application.
(It's C++ native static libraries linked into a VS2008 C++/CLR Windows Forms app with the "/clr" compiler setting)
Typical behaviour: app starts using 30 MB (private memory). Then leaks memory slowish, say a MB every hour when running under simulated heavy load. This simulates the app being live for days or weeks.
I've tried using several tools to track down memory leaks including both the CRT debugging stuff that comes with the Visual Studio CRT libs. I've also used a commercial leak detection tool ("Memory Validator").
Both report negligible memory leaks at shutdown (a few minor entries that amount to a few KB that I'm not worried about). Also, I can see when running that the tracked memory doesn't seem to amount to that much (so I don't believe it is just memory that is being held and only released at app exit). I get around 5 MB of listed memory (out of total > 30MB).
The tool (Memory Validator) is setup to track all memory usage (including malloc, new, Virtual memory allocation and a whole bunch of other types of memory allocation). Basically, every setting for which memory to track has been selected.
The .NET image reports that it is using around 1.5 MB of memory (from perfmon).
Here's a final bit of information: we have a version of the app which runs as a native console application (purely native - not CLR at all). This is 95% the same as the mixed mode except without the UI stuff. This doesn't seem to leak memory at all, and peaks at around 5MB private bytes.
So basically what I'm trying to get across here is that I don't think any of the native code is leaking memory.
Another piece of the puzzle: I found this which refers to memory leaks in mixed mode applications when targeting 2.0 framework (which I am): http://support.microsoft.com/kb/961870
Unfortunately the details are infuriatingly sparse so I'm not sure if it's relevant. I did try targeting 3.5 framework instead of 2.0 but still had the same problem (maybe I didn't do this right).
Anyone have any suggestions?
A few things that might help me:
- Are there any other kind of memory allocations that I'm not tracking?
- How come the figures don't add up? I get 5 MB of CRT memory usage, 1.5 MB of .NET memory so how come the whole app uses 30MB private bytes? Is that all tied up in the .NET framework? Why don't I see these in the leak tool? Won't the .NET framework appear as some kind of allocated memory?
- Any other leak detection tools which work well with mixed mode apps?
Thanks for any help
John
发布评论
评论(5)
您可能有参考泄漏,请查看 ANTS 分析软件。 Ants Profiler
引用泄漏相当于 .net 中的内存泄漏,你持有对一个对象的引用,这会阻止它被垃圾收集,因此你使用的内存开始增加。
You may have a reference leak, look into ANTS profiling software. Ants Profiler
A reference leak is the .net equivalent of a memory leak, you hold references to an object which stops it being garbage collected, and thus you memory in use starts to go up.
您是否可能错过了一些处理程序,如果您使用 GDI+ 和许多其他 API,则可能会发生这种情况。
如果您运行静态分析工具 FXCop,它有一条规则来检查您是否在提供接口的对象上调用了 dispose(或使用了“using”)语句。 在 .Net 中,如果函数使用非托管代码,它通常会提供 dispose 或 close 方法,以免泄漏资源/内存。
Is it possible you've missed some disposers, can happen if your using GDI+ and many other APIs.
If your run the static analysis tool FXCop it has a rule to check if you've called dispose (or used the "using") statements on your objects that provide the interface. In .Net if a function uses unmanaged code it will usually provide a dispose or close method for you to not leak the resource/memory.
好吧我终于找到问题了。
这是由于/EH(异常处理)设置不正确造成的。
基本上,对于混合模式 .NET 应用程序,您需要确保所有静态链接库均使用 /EHa 而不是默认的 /EH 进行编译。
(应用程序本身也必须使用 /EHa 进行编译,但这是给定的 - 如果您不使用它,编译器将报告错误。问题是当您链接到其他静态本机库时。)
问题是捕获异常在应用程序的托管位中,这些异常在使用 /EHs 编译的本机库中抛出,最终无法正确处理异常。 然后,无法正确调用 C++ 对象的析构函数。
就我而言,这种情况只发生在一个罕见的地方,因此我花了很长时间才发现。
OK I finally found the problem.
It was caused by an incorrect setting for /EH (Exception Handling).
Basically, with mixed mode .NET apps, you need to make sure all statically linked libs are compiled with /EHa instead of the default /EHs.
(The app itself must also be compiled with /EHa, but this is a given - the compiler will report error if you don't use it . The problem is when you link in other static native libs.)
The problem is that exceptions caught in the managed bit of the app, which were thrown within native libraries compiled with /EHs end up not handling the exception correctly. Destructors for C++ objects are not then called correctly.
In my case, this only occurred in a rare place, hence why it took me ages to spot.
就像 Spence 所说的那样,但对于 C++/CLI ;)....
对于您在 C++/CLI 中使用的任何对象,如果您从 C++ 代码创建更多该对象,您应该尝试使用堆栈分配语义,即使这是编译器的魔法之类的东西,它能够设置您可能习惯从本机代码使用的嵌套 __try {} __finally {} 语句(即以不丢失对 Dispose 的调用的方式设置它们)。
尼什的 代码项目中有关 C++/CLI 堆栈分配语义的文章非常好,深入介绍了如何使用{} 进行模拟。
您还应该确保删除实现 IDisposable 的任何对象,因为您无法在 C++/CLI 中调用 Dispose,如果您不使用堆栈语义,则删除会为您执行此操作。
我通常在 Streams 上调用 Close 自己,并尝试在以下情况下分配 nullptr我已经完成了对象的工作,以防万一。
您可能还想查看这篇关于内存问题的文章,特别是关于事件订阅者,如果您将事件分配给对象,则可能会泄漏...
作为最后的手段(或者也许是第一个:),我过去做过的一件事是使用 CLR 分析器 API,这是另一篇关于如何执行此操作的文章,作者的作者 (Jay Hilyard)有一个可以回答的例子;
许多对象实例正在被
分配?
每种类型?
GC 是否立即提供
通过垃圾收集以及什么
你可以找到?
收集对象实例?
应该让您比某些商品分析器有更好的想法,我注意到它们有时可能会产生误导,具体取决于您的分配 porofile(顺便说一句。注意大型对象堆问题,>~83kb 对象是经过特殊处理的,在这种情况下,我建议摆脱大对象堆:)。
鉴于您的评论,还有一些事情...
我之前发布过关于图像加载不收费配额或任何其他可辨别的统计数据,这意味着什么,您可能需要追踪一些句柄或加载器问题(最终参见加载器锁定),但在此之前,您可以尝试设置一些 约束执行区域,它们可以创造奇迹,但不幸的是也很难改造到非纯代码中。
最近的这篇 MSDN Mag 文章文档中有很多 perfmon 类型的内存探索(这个旧版本的后续)。
来自 VS Perf Blog,他们展示了如何在 Visual Studio 中使用 SOS,这可以很方便地追踪 rouge DLL,相关帖子也不错。
Maoni Stephen 的博客 和 公司,他说他是性能团队的一员,但基本上他的帖子 100% 都是关于 GC 的,所以他很可能写的。
Rick Byers 是 CLR 诊断团队的开发人员,他的许多博客好友都是也是很好的来源,但是,我强烈建议也参考相当新的 开发/诊断论坛。 他们最近扩大了讨论范围。
代码覆盖工具和跟踪 通常可以提供帮助,让您了解实际运行的情况。
(具体来说,这些特定的统计数据可能无法让您全面了解困扰代码的问题,我可以说,最近,我发现(即使使用 .net4beta 二进制文件,来自 这家公司,相当不错,它能够从其配置文件跟踪中得出本机/托管泄漏,带您回到精确的源代码行(即使经过优化,也相当不错(并且有 30 天的试用期))))。
祝你好运!! 希望其中一些有所帮助,这对我来说只是新鲜事,因为我现在正在做很多相同的工作;)
Like Spence was saying, but for C++/CLI ;)....
For any object which you are using in C++/CLI, if you create more that object's from you C++ code, you should try to use stack allocation seymantics, even though this is a compiler magic sort of thing, it is able to setup the nested __try {} __finally {} statements you may be used to using from native code (that is setup them in a way to not loose a call to Dispose).
Nish's article at the code project here on C++/CLI stack allocation semantics is pretty good and goes into depth about how to emulate using{}.
You should also make sure to delete any object's that implment IDisposable as you can not call Dispose in C++/CLI, delete does this for you, if your not using stack semantics..
I usually call Close myself on Streams and try to assign nullptr when I am finished with object's, just in case.
You may also want to check out this article on memory issues, perticularly about event subscribers, if you are assigning event's to your objects, you may be leaking...
As a last resort (or maybe first:), one thing I have done in the past is make use of the CLR profiler API, here's another article on how to do this, the author's writer (Jay Hilyard) has an example that answers;
many object instances are being
allocated?
of each type?
does the GC provide as it goes
through a garbage collection and what
can you find out?
collect the object instances?
Should get you a better idea than some commodity profiler, I've noticed that they can be occasionally misleading depending on your allocation porofile (btw. watch out for large object heap issues, > ~83kb objects are specially handled, in that case, I'd reccomend, getting out of the large object heap :).
Given your comments, a few more things...
I've posted before about image load's not charging quota or any other disernable statistic, what this means, you may need to track down some handle or loader issue (see loader lock eventually), but before that, you can try setting up some Constrained Execution Regions, they can work wonders, but are also unfortunately difficult to retro-fit into non-pure code.
This recent MSDN Mag, article document's a lot of perfmon type memory sperlunking (followup for this older one).
From the VS Perf Blog, they show how to use SOS in visual studio, which can be handy, to track down rouge DLL's, related posts are also good.
Maoni Stephen's Blog and company, he say's he's on the perf team, but essentially 100% of his posts are with respect to the GC so much so he may of well of wrote it.
Rick Byers is a dev with the CLR diagnostics team, many of his blog-buddies are also good source's, however, I would strongly suggest also refering to the quite new dev/diagnostics forum. They have recently expanded the scope of their discussions.
Code Coverage Tools and tracing can often help, to give you an overview of what's actually running.
(specically, those perticular stat's may not be giving you a global view of what is plauging your code, I can say that recently, I have found (even with .net4beta binaries, the profiler from this company, is quite good, it is capable of deriving native/managed leaks's from it's profile traces, brings you back to the exact source lines (even if optimized, quite nice (and it has a 30day trial)))).
Good luck!! Hope some of this helps, it's only fresh in my mind, as I am doing much of the same work right now ;)
尝试一下: 调试诊断。
生成一些内存转储后,它会给您一个关于分配了哪些内存的良好摘要,并且根据找到的 PDB,它可以告诉您谁分配了内存。
Try out: DebugDiags.
After generating some memory dumps, it will give you a nice summery of what memory was allocated, and depending on finding your PDB's, it can tell you by whom it was allocated.