我的问题的背景是:我正在转换一个消息处理应用程序,该应用程序使用从VB6到C#的许多COM组件。应用程序中的许多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中暴露的非托管资源。
答案 0 :(得分:2)
您是否在实例化COM对象的类中正确实现了IDisposable?
您需要在Dispose()方法中实现IDisposable并处理您的COM RCW。然后,实例化实现IDisposable的类的所有代码都应该显式地或使用using()语句来调用它,如下所示:
var first = new DisposableObject();
...
first.Dispose();
和
using(var first = new DisposableObject())
{
...
}
IDisposable是让CLR及时处理这些对象并确保丢失COM引用的唯一方法。
答案 1 :(得分:2)
使用Finalizer或析构函数清理COM对象使用的内存。
http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx
或者,如果您希望立即清理对象,可以实现IDispose
,并在代码中使用using
语句来实例化COM对象。