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

时间:2015-05-15 08:05:42

标签: c# .net .net-4.0 garbage-collection finalizer

我注意到在使用VS 2013在.Net 4.0中的32位和64位下编译控制台应用程序时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位模式下,许多更多对象卡在GC中,直到稍后创建更多对象(即使&#34 ;收集"和#34; WaitForPendingFinalizers"被调用)。在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位。在我的系统上,&#34;测试#9&#34;永远不会被收集(弱引用仍然存在),即使在创建了更多对象之后,又尝试了第二次。

仅供参考:提出这个问题的主要原因是因为我在我的控制台中有一个\gctest选项来测试V8.Net与本机端底层V8引擎之间的垃圾收集。它工作在64位,但不是32。

1 个答案:

答案 0 :(得分:3)

x86和x64上的VS2013 + .NET 4.5无重复:

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所注意到的),可能存在变量的“ghost”副本,它们的生命周期延长到方法结束时(在Debug模式下编译代码或者你有一个附加调试器(F5而不是Ctrl + F5),变量的生命周期一直延长到方法结束,以便于调试)

例如,对象初始化(当您执行类似new Foo {I = 5}的操作时)会创建一个隐藏的局部变量。