【问题标题】:Is this COM object subject to garbage collection?这个 COM 对象会被垃圾回收吗?
【发布时间】:2011-01-21 10:46:50
【问题描述】:

我正在使用来自 .NET 的 COM 对象,该对象使用互操作。该对象基本上从套接字获取数据并触发一些事件以供 .NET 层处理。然而,一段时间后,COM 对象停止触发事件,后来发现这是因为它被 GC 收集了。

源代码的结构类似于下面这个:

static void Main(string[] args)
{
    MyEventGen gen = new MyEventGen();
    WeakReference wr = new WeakReference(gen);
    gen.ReceiveDataArray += 
            new _IMyEventGenEvents_ReceiveDataArrayEventHandler(gen_ReceiveDataArray);
    while (true)
    {
        Thread.Sleep(1000);
        Console.WriteLine(wr.IsAlive);
    }
}

static void gen_ReceiveDataArray(ref Array indices, ref Array values)
{
    // do nothing
}

到目前为止我所知道的:

  • 据我了解,对象gen 不应以任何方式进行垃圾回收。 因为该对象在Main 范围内仍处于活动状态。但是目前的结果显示对象被GC回收了。

  • 该对象仅在构建为发布并在不调试的情况下运行时进行垃圾收集。在调试器下运行 Debug 构建/运行两种模式都可以。

  • 程序将在第一个 Gen #0 集合之后精确打印第一个“False”。

  • 通过访问while 循环中的对象,例如Console.WriteLine(gen.ToString()) ,防止它被GC'd!

  • 通过添加 Program 类的另一个静态字段来保留其引用也可以防止它被 GC。

  • 尝试不同的数据负载,我发现 GC 仅在 Private Bytes 超过 ~ 3X MB 的阈值时收集对象。

  • 检查CLRProfiler,提到的对象被怀疑被GC'd。

我是否遗漏了一些重要的 .NET GC 概念?是否可以获得对象被 GC 的原因?这可能是已知的 GC 错误吗?

我正在使用 VS 2008 + .NET 3.5 SP1。欣赏你的想法。谢谢!

【问题讨论】:

    标签: c# .net com garbage-collection com-interop


    【解决方案1】:

    无需使用 COM 对象来重现这一点。考虑以下几点:

    public class MyEventGen
    {
        public event Action ReceiveDataArray;
    }
    
    class Program
    {
        public static void Main()
        {
            var gen = new MyEventGen();
            var wr = new WeakReference(gen);
            // this is the last time we access the 
            // gen instance in this scope so after this line it
            // is eligible for garbage collection
            gen.ReceiveDataArray += new Action(gen_ReceiveDataArray);
    
            // run the collector
            GC.Collect();
            while (true)
            {
                Thread.Sleep(1000);
                Console.WriteLine(wr.IsAlive);
            }
        }
    
        static void gen_ReceiveDataArray()
        {
        }
    }
    

    在发布模式下运行它,它会表现出相同的行为。 gen 对象超出范围并被垃圾回收,因为在循环执行期间没有任何东西可以让它保持活动状态。

    【讨论】:

    • 谢谢达林!看起来这些年来我误解了 GC 过程:')
    • @m3rLinEz,问题是在发布模式下执行了许多优化,GC 可能变得非常激进。能够理解gen变量在最后一次访问后不再使用并被收集。将 gen 保留为静态变量应该可以防止这种情况发生。
    【解决方案2】:

    在执行 while 循环期间,genwr 都不会在 Main 范围内保持活动状态。 您将前三行括在大括号中,将这些变量放在子范围中,在您的 while 循环开始之前退出。

    【讨论】:

    • 我认为情况并非如此。我认为大括号是一个错字,否则他的程序将无法编译,因为他无法在 while 循环中访问 wr 变量并能够打印 False。
    • 即使作用域也不足以使对象保持活动状态:重要的是使用对象的位置(尽管 VS 调试器使对象保持活动状态直到作用域结束)。您可以在 Release 构建中获得惊喜,其中对象是在方法中间收集的,甚至是在一行代码中收集的。
    【解决方案3】:

    只要它们的 .NET 等价物从程序中消失,您的 COM 对象就有资格被销毁。最后一次使用gen 是在调用ReceiveDataArray += ... 时。

    您应该在程序中可以进行清理的位置添加对GC.KeepAlive(gen) 的调用。或者,如果它需要在程序退出之前保持活动状态,您可以将其添加为 static 字段。

    【讨论】:

    • 谢谢蒂姆!只是为了确认一下,所以我不知道局部变量引用的对象是否会持续到下一个结尾 '}' 大括号,这标志着其范围的结束?
    • 这是正确的:只要它被任何托管代码积极使用,它就会一直存在,但此后随时可能消失。仅仅被分配给一个变量并不算作“使用”:该变量必须在同一方法中被提及,以使其中的对象保持活动状态。
    猜你喜欢
    • 1970-01-01
    • 2019-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多