从.NET安全地释放COM对象引用

时间:2009-12-02 18:41:30

标签: .net com-interop rcw

我已经在网上阅读了很多关于安全发布RCW的文章,在我看来,没有人能够确切地知道需要按什么顺序做什么,所以我要问你们各位的意见。例如,可以这样做:

object target = null;
try {
    // Instantiate and use the target object.
    // Assume we know what we are doing: the contents of this try block
    // do in fact represent the entire desired lifetime of the COM object,
    // and we are releasing all RCWs in reverse order of acquisition.
} finally {
    if(target != null) {
        Marshal.FinalReleaseComObject(target);
        target = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

然而,有些人主张在Marshal.FinalReleaseComObject之前进行垃圾收集,有些人之后,有些人根本没有。是否真的有必要手动GC每个RCW,特别是在它已经从COM对象中分离出来之后?

在我看来,将RCW从COM对象中分离并让RCW自然过期会更简单,更容易:

object target = null;
try {
    // Same content as above.
} finally {
    if(target != null) {
        Marshal.FinalReleaseComObject(target);
    }
}

这样做是否足够?

1 个答案:

答案 0 :(得分:12)

要发布对目标COM对象的引用,只需调用Marshal.FinalReleaseComObject强制收集即可和首选。换句话说,您已经完成了在完成参考后立即发布参考的责任。我不会触及FinalReleaseComObject vs ReleaseComObject的问题。

这就留下了一个更大的问题:为什么人们主张调用GC.Collect()WaitForPendingFinalizers()

因为对于某些设计,很难知道何时没有更多托管引用,因此您无法安全地调用ReleaseComObject。你有两个选择,让内存积累并希望收集发生或强制收集。 [请参阅评论中的Steven Jansen的投票说明]

另外需要注意的是,将target设置为null通常是不必要的,在示例代码中特别没有必要。将对象设置为空是VB6的常见做法,因为它使用基于引用计数的垃圾收集器。 C#的编译器非常聪明(在构建发布时),知道target在上次使用后无法访问,并且可能是GC,甚至在离开范围之前。最后一次使用,我的意思是最后一次使用,所以在某些情况下您可能会将其设置为null。您可以使用以下代码自行查看:

   using System;
   class GCTest
   {
       ~GCTest() { Console.WriteLine("Finalized"); } 
       static void Main()
       {
           Console.WriteLine("hello");
           GCTest x = new GCTest();
           GC.Collect();
           GC.WaitForPendingFinalizers();
           Console.WriteLine("bye");
       }
   }

如果您构建版本(例如,CSC GCTest.cs),“Finalized”将在“hello”和“bye”之间打印出来。如果你构建调试(例如,CSC / debug GCTest.cs),“Finalized”将在“bye”之后打印出来,而在x之前将Collect()设置为null会“固定”那个。