GC行为在32位和64位应用之间不一致



我注意到使用VS 2013的32位和64位在.NET 4.0中汇编控制台应用时,GC的行为不一致。

考虑以下代码:

class Test
{
    public static bool finalized = false;
    ~Test()
    {
        finalized = true;
    }
}

Main() ...

var t = new Test();
t = null;
GC.Collect();
GC.WaitForPendingFinalizers();
if (!Test.finalized)
    throw new Exception("oops!");

在64位(调试)模式下运行时,每次毫无失败;但是,以32位模式运行,我无法强迫此对象收集(即使我创建了更多对象并等待一段时间,我已经尝试过)。

有人对为什么这样做有任何想法吗?试图调试一些必须处理32位版本的组件版本的代码时,这给我带来了麻烦。32位模式下有很多对象,直到很长时间后才坐在那里(64位)。

我试图以32位模式调试一些内容,但最终化器没有被调用(至少不是用力)。这些对象只是坐在那里而永远不会收集(我可以看到所有弱参考仍然具有价值)。在64位模式下,所有弱参考都按预期清除,所有最终化器都被调用。

注意:尽管上面的代码规模很小,但我已经注意到32位模式下的许多 More 对象粘在GC中,直到以后创建更多对象(即使"收集"时,和" Waitforpending -Finalizers"被称为)。在64位模式下从来都不是这种情况。我有一个用户想知道为什么这么多未收集的对象,这导致我进行了调查,我发现一切似乎在64位模式下都比32.试图理解原因更好。

编辑:这是显示差异的更好代码:

class Program
{
    class Test
    {
        public static bool Finalized = false;
        public int ID;
        public Test(int id)
        {
            ID = id;
        }
        ~Test()
        { // <= Put breakpoint here
            Finalized = true;
            Console.WriteLine("Test " + ID + " finalized.");
        }
    }
    static List<WeakReference> WeakReferences = new List<WeakReference>();
    public static bool IsNet45OrNewer()
    {
        // Class "ReflectionContext" exists from .NET 4.5 onwards.
        return Type.GetType("System.Reflection.ReflectionContext", false) != null;
    }
    static void Main(string[] args)
    {
        Console.WriteLine("Is 4.5 or newer: " + IsNet45OrNewer());
        Console.WriteLine("IntPtr: " + IntPtr.Size + Environment.NewLine);
        Console.WriteLine("Creating the objects ...");
        for (var i = 0; i < 10; ++i)
            WeakReferences.Add(new WeakReference(new Test(i)));
        Console.WriteLine("Triggering collect ...");
        GC.Collect();
        Console.WriteLine("Triggering finalizers ..." + Environment.NewLine);
        GC.WaitForPendingFinalizers();
        Console.WriteLine(Environment.NewLine + "Checking for objects still not finalized ...");
        bool ok = true;
        for (var i = 0; i < 10; ++i)
            if (WeakReferences[i].IsAlive)
            {
                var test = (Test)WeakReferences[i].Target;
                if (test != null)
                    Console.WriteLine("Weak references still exist for Test " + test.ID + ".");
                ok = false;
            }
        if (ok)
            Console.WriteLine("All Test objects successfully collected and finalized.");
        Console.WriteLine(Environment.NewLine + "Creating more objects ...");
        for (var i = 0; i < 10; ++i)
            WeakReferences.Add(new WeakReference(new Test(i)));
        Console.WriteLine("Triggering collect ...");
        GC.Collect();
        Console.WriteLine("Triggering finalizers ..." + Environment.NewLine);
        GC.WaitForPendingFinalizers();
        Console.WriteLine(Environment.NewLine + "Checking for objects still not finalized ...");
        ok = true;
        for (var i = 0; i < 10; ++i)
            if (WeakReferences[i].IsAlive)
            {
                var test = (Test)WeakReferences[i].Target;
                if (test != null)
                    Console.WriteLine("Weak references still exist for Test " + test.ID + ".");
                ok = false;
            }
        if (ok)
            Console.WriteLine("All Test objects successfully collected and finalized.");
        Console.WriteLine(Environment.NewLine + "Done.");
        Console.ReadKey();
    }
}

它在64位,但在32位中不起作用。在我的系统上,"测试#9"永远不会收集(即使创建更多对象并尝试第二次,也没有收集薄弱的参考)。

fyi:提出问题的主要原因是因为我的控制台中有一个gctest选项,可以在天然侧的V8.NET和基础V8引擎之间测试垃圾收集。它起作用的64位,但不工作32。

x86和x64上的VS2013 .net 4.5 no repro:

class Program
{
    class Test
    {
        public readonly int I;
        public Test(int i)
        {
            I = i;
        }
        ~Test()
        {
            Console.WriteLine("Finalizer for " + I);
        }
    }
    static void Tester()
    {
        var t = new Test(1);
    }
    public static bool IsNet45OrNewer()
    {
        // Class "ReflectionContext" exists from .NET 4.5 onwards.
        return Type.GetType("System.Reflection.ReflectionContext", false) != null;
    }
    static void Main(string[] args)
    {
        Console.WriteLine("Is 4.5 or newer: " + IsNet45OrNewer());
        Console.WriteLine("IntPtr: " + IntPtr.Size);
        var t = new Test(2);
        t = null;
        new Test(3);
        Tester();
        Console.WriteLine("Pre GC");
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("Post GC");
        Console.ReadKey();
    }
}

请注意,(如@sriram Sakthivel所注意到的),可能会有您的变量的"幽灵"副本,它们的寿命延长到方法结束(当您以调试模式编译代码或您有一个时,附加的调试器(F5而不是Ctrl F5),变量的寿命已延长,直到方法结束以减轻调试)

例如,对象初始化(当您执行类似New Foo {i = 5})的事情会创建一个隐藏的本地变量。

最新更新