.NET垃圾收集器的怪异行为

埃尔穆(Elmue)

MSDN说 GC.Collect()

所有对象,无论它们在内存中已经存放了多长时间,都将被考虑进行收集;但是,不会收集托管代码中引用的对象使用此方法可以强制系统尝试回收最大可用内存量。

因此,我希望在收集Parent之前不收集仍在Parent类中引用的Child类。

但是奇怪的是,它大部分是在收集父母之前收集的。这对我来说没有任何意义。

我在VS2010上编译以下代码,并在Framework 4.0上运行它。我得到的是:

垃圾收集器测试代码

using System;

namespace GarbageCollector
{
    class Child
    {
        public bool bInUse = true;
        public void Dispose()
        {
            Console.WriteLine("Child finished by Parent.");
            bInUse = false;
        }

        ~Child()
        {
            bInUse = false;
        }
    }

    class Parent
    {
        Child child = new Child();
        ~Parent()
        {
            if (!child.bInUse)
                Console.WriteLine("Finalizing Child that is still in use in a Parent!");

            child.Dispose();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                for (int i=0; i<10; i++)
                {
                     Parent P = new Parent();
                }
                GC.Collect();
            }
        }
    }
}

有人可以解释一下这是怎么回事吗?


编辑:

我已经找到了解决问题的方法。如果要访问类的终结器中的类成员,如果这些成员本身也具有终结器,则可能会出现问题。在这种情况下,成员可能已经死亡,因为GarbageCollector会以任意顺序销毁它们,因此您的类的终结器无法访问它们。(父母前子女或父母后子女)

但是,如果您访问的类成员没有自己的终结器,则不会出现此问题。

因此,如果您想在类中存储例如句柄列表,并且要在Finalizer中关闭这些句柄,请确保此列表类没有自己的Finalizer,否则您的句柄可能会消失,然后再关闭它们!

务实的

因此,我希望在收集Parent之前不收集仍在Parent类中引用的Child类。

那不是一个真实的假设。GC可以自由收集任何对象,只要它可以证明该对象不再可从将来在任何时候运行的任何代码访问。这是允许的,在这一点上收集任何对象,但它是免费的收集,或假,任何物体的满足这一条件。如果某个对象引用了另一个对象,但是它们都不是根对象,也不能从任何根对象中访问,则GC可以自由地以任何顺序删除它们,甚至可以删除子对象而不是父对象。

还值得注意的是您的代码未显示任何内容。对象的终结器可以在符合收集条件的条件与实际收集条件之间的任何时间运行。即使同时运行这两个终结器,终结器的运行顺序也不保证是对象本身的收集顺序。

当然,在实践中,赔率是非常高的,这两个目标实际上在收集准确的同时,除非其中一个对象已经存在了比其他长得多。GC的运行方式是将所有对象(给定层中的对象)视为“失效”,然后将仍“处于活动状态”的那些对象复制到新部分中,从而在需要某项内存时将未复制的所有对象都覆盖掉,因此,如果两个对象都在同一个GC层中(可能),那么两个位置的内存都可以在同一时间完全覆盖至于何时实际覆盖该内存,甚至很难找出(即使它曾经被覆盖)。

因此,最终,在许多不同的层面上,所引用的期望背后的整个概念并不是一个明智的前提。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章