【发布时间】:2013-08-24 09:46:42
【问题描述】:
我最近遇到了这个SO article 并针对我的以下场景进行了调整:
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);
}
}
}
}
运行此程序会得到以下预期结果:
True
False
Press any key to continue . . .
但是改变这个:
var refContainer = new RefContainer();
refContainer.Target = obj;
对此(使用对象初始化器语法):
var refContainer = new RefContainer() { Target = obj };
给出以下输出:
True
True
Press any key to continue . . .
这里发生了什么?为什么引用的生命周期会因为使用 Object Initializer 而不同?
【问题讨论】:
-
嗯...它为我打印“假,假”。我根本无法让它打印 True 。你是在调试器下运行这个吗?调试还是发布版本?
-
@JonSkeet-在调试器下运行,是的。
-
我用单元测试进行了尝试,结果与发布的相同。这很酷,这就是为什么你不应该搞乱 GC :D
-
@JonSkeet-我在发布模式下重新编译,现在我得到了 False,False
-
@Jim 发生这种情况的原因是,当附加了调试器时,GC 必须假定您可以随时暂停程序并在任何先前声明但不再使用的变量上添加一个监视变量。因此,只要您有可能在调试器中查看它,该对象就会一直保持活动状态。通过在没有附加调试器的情况下运行,GC 知道没有人会暂停程序并附加到监视变量,因此一旦代码中不再使用该对象,它就可以立即进行收集。
标签: c# garbage-collection weak-references object-initializers