C# 短弱引用何时变为空?
我在类C# 短弱引用何时变为空?,c#,memory-management,garbage-collection,weak-references,C#,Memory Management,Garbage Collection,Weak References,我在类Foo中使用WeakReference()跟踪对象。这个类有一个析构函数,我需要在其中访问被跟踪的对象。我跟踪的对象也在使用WeakReference跟踪Foo 所以现在我想知道,WeakReference的“归零”到底是什么时候发生的?是否所有WeakReference在任何终结器运行之前都被置空,还是在它们跟踪的对象的终结器即将运行之前都被置空 更新 现在我还想知道Monoproject是否能对这一点(,)有所帮助。但是我有点担心,可能MS-GC和Mono-GC处理这个问题的方式不同,
Foo
中使用WeakReference
()跟踪对象。这个类有一个析构函数,我需要在其中访问被跟踪的对象。我跟踪的对象也在使用WeakReference
跟踪Foo
所以现在我想知道,WeakReference
的“归零”到底是什么时候发生的?是否所有WeakReference
在任何终结器运行之前都被置空,还是在它们跟踪的对象的终结器即将运行之前都被置空
更新
现在我还想知道
Mono
project是否能对这一点(,)有所帮助。但是我有点担心,可能MS-GC
和Mono-GC
处理这个问题的方式不同,并且不兼容。您可以用一个简单的测试程序自己验证。但是我发现WeakReference
类型本身的文档比您看到的页面更清晰
特别是,在链接页面中称为“短”和“长”的标志在中称为trackresurvation
。参数的说明如下所示:
指示何时停止跟踪对象。如果为true,则在完成后跟踪对象;如果为false,则仅跟踪对象直到完成
“备注”一节还写道:
如果TrackResurvation为false,则会创建一个简短的弱引用。如果TrackResurvation为true,则会创建一个长弱引用
这确认了当您使用“短”弱引用时,WeakReference
对象将不再跟踪最终确定的对象(即,目标
变为null
),但当您使用“长”弱引用时,它将被跟踪
对于这两种弱引用,实际上已被垃圾收集的对象肯定不再被跟踪(显然)
通常,当终结器线程实际执行其工作时,程序中的任何其他线程都不能观察对象,因此当
Target
属性设置为null
时,“短”弱引用的精确时刻对我来说似乎没有意义。如果程序中的其他线程观察到该值为非null,则终结器尚未运行。如果它将其观察为null,则终结器已运行。“其他线程”本身不应该在终结器线程工作时运行,因此就“其他线程”而言,终结本质上应该是原子的。我想写一个演示程序来演示这一区别。结果比我想象的更具挑战性。第一个必要的因素是确保终结器线程的速度可以减慢,这样您就可以观察WeakReference.IsAlive的值,而不必冒与终结器线程争用的风险。所以我用了:
class FinalizerDelayer {
~FinalizerDelayer() {
Console.WriteLine("Delaying finalizer...");
System.Threading.Thread.Sleep(500);
Console.WriteLine("Delay done");
}
}
然后一个小类将成为WeakReference的目标:
class Example {
private int instance;
public Example(int instance) { this.instance = instance; }
~Example() {
Console.WriteLine("Example {0} finalized", instance);
}
}
然后是一个演示长弱引用和短弱引用之间差异的程序:
class Program {
static void Main(string[] args) {
var target1 = new Example(1);
var target2 = new Example(2);
var shortweak = new WeakReference(target1);
var longweak = new WeakReference(target2, true);
var delay = new FinalizerDelayer();
GC.Collect(); // Kills short reference
Console.WriteLine("Short alive = {0}", shortweak.IsAlive);
Console.WriteLine("Long alive = {0}", longweak.IsAlive);
GC.WaitForPendingFinalizers();
Console.WriteLine("Finalization done");
GC.Collect(); // Kills long reference
Console.WriteLine("Long alive = {0}", longweak.IsAlive);
Console.ReadLine();
}
}
必须运行此程序,以便调试器不会影响对象的生存期。选择发布版本并更改调试器设置:工具+选项、调试、常规,取消选中“抑制JIT优化”选项
事实证明,对象的终结顺序确实是不确定的。每次运行程序的顺序都不同。我们希望首先完成FinalizerDelayer对象,但这并不总是发生。我认为这是内置地址空间布局随机化功能的一个副作用,它使托管代码很难受到攻击。但经常运行它,您最终会得到:
延迟终结器…短活动=假
长寿=真
延迟完成
示例1已定稿
示例2已定稿
完成定稿
长寿=假
长话短说:
- 一旦对象被收集并放置在freachable队列上,一个简短的弱引用就会将IsAlive设置为false,以便最终确定它。该对象在物理上仍然存在,但不再存在强引用,它将很快完成
- 长弱引用在对象的真实生存期(包括其在freachable队列上的生存期)中全程跟踪对象。IsAlive在完成其终结器之前不会设置为false
当对象被复活时要小心一个怪癖,当强引用被重新创建时,要从freachable队列移回正常堆。这不是我在这个演示程序中探讨的东西,但是需要一个很长的弱参考来观察它。你需要一个很长的弱引用的基本原因。经过大量研究,我找到了这篇古老的文章(讨论复活和freachable队列): 下面是垃圾收集(GC)运行时发生的情况: