为什么使用Object Initializer保持对象存活?

Jim*_*Jim 5 c# garbage-collection weak-references object-initializers

我最近遇到了这篇SO文章并根据我的场景进行了调整:

using System;
using System.Collections.Generic;

namespace ConsoleApplication18
{
    class Program
    {
        static void Main(string[] args)
        {
            Manager mgr = new Manager();
            var obj = new byte[1024];

            var refContainer = new RefContainer();
            refContainer.Target = obj;

            obj = null;

            mgr["abc"] = refContainer.Target;

            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            Console.WriteLine(mgr["abc"] != null); // true (still ref'd by "obj")

            refContainer = null;

            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            Console.WriteLine(mgr["abc"] != null); // false (no remaining refs)           
        }
    }

    class RefContainer
    {
        public object Target { get; set; }
    }

    class Manager
    {
        Dictionary<string, WeakReference> refs =
        new Dictionary<string, WeakReference>();
        public object this[string key]
        {
            get
            {
                WeakReference wr;
                if (refs.TryGetValue(key, out wr))
                {
                    if (wr.IsAlive)
                        return wr.Target;
                    refs.Remove(key);
                }
                return null;
            }
            set
            {
                refs[key] = new WeakReference(value);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

运行此程序会产生以下预期结果:

True
False
Press any key to continue . . .
Run Code Online (Sandbox Code Playgroud)

然而改变这个:

var refContainer = new RefContainer();
refContainer.Target = obj;
Run Code Online (Sandbox Code Playgroud)

为此(使用Object Initializer语法):

var refContainer = new RefContainer() { Target = obj };
Run Code Online (Sandbox Code Playgroud)

给出以下输出:

True
True
Press any key to continue . . .
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?为什么引用的生命周期因使用Object Initializer而不同?

Jon*_*eet 7

为什么引用的生命周期因使用Object Initializer而不同?

无论如何我无法重现你的问题,但我怀疑是因为这个:

var refContainer = new RefContainer() { Target = obj };
Run Code Online (Sandbox Code Playgroud)

相当于:

var tmp = new RefContainer();
tmp.Target = obj;
var refContainer = tmp;
Run Code Online (Sandbox Code Playgroud)

...所以你最终得到了对堆栈上对象的额外引用.现在在调试器下不运行时,我想到了GC注意到,该堆栈位置是永远不会再次读取,并允许垃圾回收的对象-但正如你在调试器下运行时,GC是比较保守的,我怀疑它将所有堆栈变量视为GC根.

这只是一个猜测 - 无论如何都无法重现它,很难肯定地说.

编辑:您在非调试模式下的分配obj = null;refContainer = null;无意义; 因为无论如何都不会在该点之后读取变量,GC会将它们忽略为GC根.