问题描述
|
我的问题的背景是:我正在将使用许多COM组件的消息处理应用程序从VB6转换为C#。应用程序中的许多COM组件都是细粒度的组件,它们在消息处理循环中以高频率频繁使用。与VB6应用程序相比,在C#应用程序中处理一组测试消息时,我看到内存使用量大量增加(并在不断增长)。我在应用程序上使用了一个内存探查器,以确认内存使用率高是由于该应用程序的非托管堆上的COM对象的活动实例引起的。我知道这些组件由于实时引用而不会“泄漏”,因为如果我将GC.Collect()放在消息处理循环的核心,则内存使用率是稳定的,几乎与VB6应用程序相同(尽管性能会像人们期望的那样急剧下降)。
我已经阅读了在C#,运行时可调用包装程序,非托管资源内存分配,Marshal.ReleaseComObject(),Marshal.FinalReleaseComObject()等可在多代垃圾收集器上找到的所有内容。它们都没有解释应用程序为何坚持使用当相应的RCW可以进行垃圾回收时,非托管堆中的活动COM对象。
在某些文章中,我看到了C#中垃圾收集器的实际实现可能涉及优化的可能性,例如不执行特定世代中所有合格对象的收集。如果这是正确的,则可以解释为什么没有收集和销毁合格的RCW及其对应的COM对象。另一个解释可能是,非托管堆中COM对象的破坏没有直接与其对应的RCW的集合相关联。我还没有找到任何有关在.NET中如何处理COM对象的详细程度的信息。我需要更好地理解这一点,因为当前我的应用程序的内存使用情况是不可接受的。任何指针或建议,将不胜感激。
编辑:我应该补充一点,我对终结器(没有记录在RCW上)和Idisposable接口(RCW没有实现)非常熟悉。据我所知,Marshal.ReleaseComObject()是显式“处置” COM引用的正确方法。我小心地为应用程序中COM对象的每个已知用法添加了该语句,这导致内存使用率没有差异。
此外,不清楚的是,当添加显式GC.Collect()不会导致内存问题时,为什么缺乏处理代码或完成代码可能会成为问题。 dispose()或终结器的存在都不会导致对象的实际集合。前者允许对象抑制其完成步骤(如果有),后者允许清除未托管资源,而这些资源都不暴露在RCW中。
解决方法
您是否在实例化COM对象的类中正确实现了IDisposable?
您需要实现IDisposable并在Dispose()方法中处理COM RCW \。然后,所有实例化实现IDisposable的类的代码都应显式调用或使用using()语句来调用它,如下所示:
var first = new DisposableObject();
...
first.Dispose();
和
using(var first = new DisposableObject())
{
...
}
IDisposable是使CLR及时处理这些对象并确保您丢失COM引用的唯一方法。
,使用终结器或析构函数来清理COM对象使用的内存。
http://msdn.microsoft.com/zh-CN/library/66x5fx1b.aspx
另外,如果您希望立即清除对象,则可以实现IDispose
,并在实例化COM对象的代码中使用using
语句。