作为this question的后续内容,我有以下代码:
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
class Child
{
public override string ToString()
{
return "I am a child!";
}
~Child()
{
Console.WriteLine("~Child called");
}
}
class Test
{
readonly object _child;
readonly WeakReference _ref;
readonly GCHandle _gch; // GCHandle is a value type, so it's safe
public Test()
{
_child = new Child();
_ref = new WeakReference(_child);
_gch = GCHandle.Alloc(_child);
}
// ...
public void DoTest()
{
lock (_child)
{
Console.WriteLine("DoTest called, child: " + _child.ToString() + ", is alive: " + _ref.IsAlive);
}
}
~Test()
{
Console.WriteLine("~Test starts");
DoTest();
_gch.Free();
Console.WriteLine("~Test ends");
}
}
static void Main(string[] args)
{
var test = new Test();
test.DoTest();
test = null;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
System.Threading.Thread.Sleep(1000);
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
Console.ReadLine();
}
}
}
输出:
DoTest called, child: I am a child!, is alive: True ~Test starts DoTest called, child: I am a child!, is alive: False ~Test ends ~Child called
问题:为什么WeakReference.IsAlive
的{{1}}成为_child
false
,而~Test()
对象仍然固定向下_child
?
答案 0 :(得分:2)
好吧,我记得访问"类实例变量"从终结器中获取并不是一个好主意,因为它们可以随机使用#34;州? 这基本上意味着WeakReference终结器将在您的课程终结器之前调用。
有一个特殊的运行时线程专用于调用Finalize 方法。当可释放队列为空时(通常是 case),这个线程睡觉了。但是当条目出现时,这个线程会唤醒, 从队列中删除每个条目,并调用每个对象的Finalize 方法。因此,您不应在Finalize中执行任何代码 对执行该线程的线程做出任何假设的方法 码。 例如,避免访问中的线程本地存储 最终确定方法。
http://msdn.microsoft.com/en-us/magazine/bb985010.aspx
如果您确定了WeakReference,您可以获得更有意义的结果:
public Test()
{
_child = new Child();
_ref = new WeakReference(_child);
_gch = GCHandle.Alloc(_child);
_test = GCHandle.Alloc(_ref);
}
如果您让GC知道现在无法收集WeakReference类 ITSELF ,您可以获得相同的结果:
static void Main(string[] args)
{
var test = new Test();
var win = new WeakReference(test._child);
test._ref = win;//new WeakReference(test._child);
test.DoTest();
test = null;
}
来自WeakReference的实际代码:
~WeakReference() {
IntPtr old_handle = m_handle;
if (old_handle != IntPtr.Zero) {
if (old_handle == Interlocked.CompareExchange(ref m_handle, IntPtr.Zero, old_handle))
GCHandle.InternalFree(old_handle);
}
}
你可以看到它在终结器运行后释放手柄,将其设置为零和IsAlive现在会报告错误。引用本身实际上是活着的。