我最近遇到了这个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;
对此(使用Object Initializer语法):
var refContainer = new RefContainer() { Target = obj };
提供以下输出:
True
True
Press any key to continue . . .
这里发生了什么?为什么引用的生命周期因使用Object Initializer而不同?
答案 0 :(得分:7)
为什么引用的生命周期因使用Object Initializer而不同?
无论如何我无法重现你的问题,但我怀疑是因为这个:
var refContainer = new RefContainer() { Target = obj };
相当于:
var tmp = new RefContainer();
tmp.Target = obj;
var refContainer = tmp;
...所以你最终得到了对堆栈上对象的额外引用。现在当不在调试器下运行时,我期望 GC注意到堆栈位置永远不会被再次读取,并允许对象被垃圾收集 - 但是当你在调试器下运行时,GC更保守,我怀疑它将所有堆栈变量视为GC根。
这只是一个猜测 - 无论如何都无法重现它,很难肯定地说。
编辑:obj = null;
和refContainer = null;
的分配在非调试模式下毫无意义;因为在该点之后变量不是 read ,GC会将它们忽略为GC根。