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

Chr*_*ter 6 .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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

这样做是否足够?

Ton*_*Lee 12

为了使您参考发布的目标COM对象,它是足够的和优选只是调用Marshal.FinalReleaseComObject没有强制收集.换句话说,您已经完成了在完成参考后立即发布参考的责任.我不会触及FinalReleaseComObjectvs 的问题ReleaseComObject.

这留下的,为什么人们主张调用更大的问题GC.Collect()WaitForPendingFinalizers()

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

另外一个值得注意的是,设置targetnull通常是不必要的,特别是在你的示例代码不必要的.将对象设置为空是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");
       }
   }
Run Code Online (Sandbox Code Playgroud)

如果你构建版本(例如,CSC GCTest.cs),"Finalized"将在"hello"和"bye"之间打印出来.如果你构建调试(例如,CSC/debug GCTest.cs),"Finalized"将在"bye"之后打印出来,而x在之前设置为null Collect()将会"修复"它.