C#中使用大量COM对象导致内存占用过高

3
我有一个应用程序最初是用VB6编写的,我使用了一个工具将其转换为C#,从功能角度来看效果相当不错。它处理大量的消息,使用许多小到中等大小的COM(C ++)对象。
我注意到在旧的VB6应用程序中运行的特定测试运行只使用不到40M的内存,在C#应用程序中需要近900M。如果我在C#应用程序的最内层消息处理循环中放置GC.Collect(),它使用的内存与VB6应用程序相同或更少,尽管它变得非常缓慢。这使我相信在绝对意义上没有“泄漏”。
然后,我通过AQTime内存分析器运行了C#应用程序,并报告说堆上有过多的COM / C ++对象。我假设这是因为COM对象周围的运行时可调用包装很小,即使它们引用的COM对象实际上更大,它们也很少(或根本不)在C#中触发收集。我认为我可以通过在C#应用程序中添加显式的Marshal.ReleaseComObject()调用来解决这个问题。我去了很多地方做了这个,在那里COM对象的生命周期很容易确定。我注意到内存使用量仅略有减少。
我想知道为什么我没有更好的成功。查看Marshal类中的静态方法,我发现有些方法让我相信我可能错过了对COM引用处理的一些微妙之处,或者我的假设是当RCW的引用计数达到零时它们会立即被销毁是不正确的。
我希望能得到其他尝试的建议或者我可能忽视或误解的其他事情。

如果不执行GC.Collect会发生什么?会崩溃吗? - John Saunders
如果我删除GC.Collect()调用,内存使用量会增加到约900M。虽然它从未崩溃过,但这种内存使用量在旧的VB6应用程序中使用相同的COM对象时少于40M是不可接受的。 - Dan Hermann
@Dan:这是虚拟内存,而不是物理内存。过多的分页会比总内存“使用量”更好地衡量。 - John Saunders
嗯,我是根据任务管理器报告的内存使用情况来计算的,这可能不是最准确的,但根据 AQTime 的数据,托管堆大小远远低于 100M,因此 COM 对象的大小大约是其 10 倍。一旦 RCW 的引用计数达到零,它们不应立即释放吗? - Dan Hermann
1
对于这种情况,任务管理器完全不准确。请使用其他内存分析工具。 - Cody Gray
@Dan:在没有实际需要进行性能分析之前,我不会费心去做。你可能会发现自己在解决错误的问题上做得很出色。经验是有说服力的。 - John Saunders
1个回答

3

很抱歉只提供链接而不是好的概述,但我自己在处理IE和mshtml时从未遇到过这个问题。

文章指出:

在.NET应用程序中使用COM对象时,涉及到两个对象:RCW和COM对象(或多个对象)。垃圾回收仅意识到RCW的大小(可能很小),而不是COM对象的大小(可能很大)。因此,尽管.NET应用程序可能释放了RCW,但垃圾回收可能无法在内存耗尽时回收RCW。只要RCW保留在内存中,它所管理的COM对象也会保留在内存中。

有两种机制可确保释放COM对象的内存:AppDomain对象和ReleaseComObject方法。使用AppDomain提供了最简单的解决方案来管理COM对象,但具有性能成本并可能暴露安全风险。使用ReleaseComObject可以避免这些成本,但需要更仔细的规划和编码。


谢谢,我之前不知道有AppDomain选项,但是对于我的情况可能不是最理想的选择,因为内部消息处理循环中性能很重要。 - Dan Hermann
@Dan - 我觉得你的循环创建和释放了相当多的COM对象。如果是这样,那么这本身就不是一个非常高效的做法。我认为ReleaseComObject是一个非常值得实现和分析的好选择。 - Ritch Melton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接