弱引用
当一个根指向一个对象时,该对象不可能被垃圾收集器收集,在这种情况下,通常说存在一个该对象的强引用(strong reference)。垃圾收集器还支持弱引用(weak reference)的概念。弱引用允许垃圾收集器收集对象,同时也允许应用程序访问该对象,结果取决于时间。
如果对象的弱引用存在,那么在垃圾收集执行后,该对象的内存将被执行,另一方面,要访问一个弱引用对象,应用程序必须获取该对象的一个强引用。如果应用程序在对象被执行之前得到了它的强引用,那么垃圾收集器将不再对该对象执行垃圾收集,因为这时存在一个该对象的强引用。
下面代码展示了弱引用的使用
class Program
{
static void Main(string[] args)
{
SomeWeakReferenceMethod();
SomeStrongtReferenceMethod();
}
static void SomeWeakReferenceMethod()
{
//创建一个强引用对象
SomeClass o = new SomeClass();
WeakReference wr = new WeakReference(o);
o = null;//移除对象的强引用
o = wr.Target as SomeClass;
if (o == null)
{
//出现过垃圾回收,对象的内存已经被回收
Console.WriteLine("已经被回收");
}
else
{
//未出现垃圾回收
Console.WriteLine("没有被回收"+o.X.ToString());
}
}
static void SomeStrongtReferenceMethod()
{
//创建一个强引用对象
SomeClass o = new SomeClass();
o = null;//移除对象的强引用
try
{
Console.WriteLine(o.X);
}
catch
{
Console.WriteLine("error");
}
}
}
public class SomeClass
{
private int x;
public int X
{
get
{
return this.x;
}
}
public SomeClass()
{
x = 5;
}
}
个人感觉弱引用机制有点像缓存,将一些操作起来比较费时(比如便利系统的硬盘)的东西{dy}次获取之后暂时放入缓存中,并且将其引用置为null来减少应用程序的压力,但是放置在缓存中可以再次使用(减少读取时的压力),至于是否能够再次使用成功要看垃圾收集器。如果执行过垃圾收集,弱引用的对象将会被回收掉当然就无法再次使用了。
System.WeakReference有两个公用构造器:
- public WeakReference(object target);
- public WeakReference(object target, bool trackResurrection);
target表示要追踪的对象(上例中为SomeClass), trackResurrection表示是否要追踪对象的复苏,换句话说就是对象在执行Finalize方法后,是否还要追踪该对象(对象执行Finlize后对象复苏了) 。
将不追踪对象复苏的WeakReference称为短弱引用(short weak reference),而将追踪对象复苏的称为长弱引用(long weak reference)。如果一个对象没有Finalize方法,长短弱引用是一样的,{zh0}不要使用长弱引用,因为长弱引用在一个对象被执行终止后允许该对象复苏,将会导致对象的状态不可预知。
一旦创建了对象的弱引用,通常将该对象的强引用设置为null,为了再次使用该对象,需要将弱引用转换为一个强引用,通过WeakReference的Target属性来完成的。如果target为null,那么对象已经被执行了垃圾收集,否则将会得到该对象的强引用。这时对象将不会被垃圾收集器收集了。
弱引用的内部机理
需要再次探究托管堆。托管堆中包含了两个内部数据结构来管理弱引用。即短弱引用表和长弱引用表。这两个表包含着一些指针,他们引用着托管堆对象。
当创建一个WeakReference对象时,它会在两个弱引用表中选择一个(长短弱引用相应对应),并在其中寻找一个空白插槽。该插槽的值将被设为我们希望追踪对象的地址---也就是new WeakReference构造的那个对象的地址,弱引用表将不会认为是应用程序的根,否则垃圾收集器将不能收集它们中的指针引用的对象。
垃圾收集器运行时发生的一系列事情:
- 垃圾收集器构造一个可达对象的图();
- 垃圾收集器扫描短弱引用表,如果表中的对象不是前面可达对象图的一部分(对象已经不是根,在上面的例子中o已经被置为null了),那么表示该对象是一个不可达的对象,将短弱引用表中对应插槽的值将被设置为null(Target为null了);
- 垃圾收集器扫描终止化链表()。如果该链表中指针引用的对象不是可达对象图的一部分,那么该对象将是不可达对象,它将被从终止化链表转移到终止化可达队列上。这时对象由成为可达对象图的一部分了。
- 垃圾收集器扫描长弱引用表,如果表中的对象不是前面可达对象图的一部分(该图现在已经包括终止化可达队列中引用的对象了),那么表示该对象是一个不可达的对象,将长弱引用表中对应插槽的值将被设置为null(Target为null了);
- 垃圾收集器压缩内存,填充不可达对象空出的位置。
继续分析代码
if (o == null)
{
//出现过垃圾回收,对象的内存已经被回收
Console.WriteLine("已经被回收");
}
else
{
//未出现垃圾回收
Console.WriteLine("没有被回收"+o.X.ToString());
}
因为在此之前o已经被置为null了,所以它已经是一个不可达对象,如果执行垃圾收集器,o自然不在可达对象图中,那么垃圾收集器将会将短弱引用表中插槽对应的值设为null,这样Target将返回null,上面代码自然会执行if里面的代码。如果垃圾收集器还没有执行,虽然o已经为null,但是短弱引用插槽依然保存着对象的引用,那么Target将会返回对象的引用,使对象继续可达,可以使用,当然上面代码就会执行else里面的代码。
短弱引用并不追踪对象的复苏。只要垃圾收集器判断对象成为不可达的对象,它就会把短弱引用表中对应的指针设置为null。如果对象重写了Finalize方法,那么这时该方法还没有被调用,所以对象仍然存在。此时target仍然返回null,虽然这时对象已经进入终止化可达队列,对象仍然存在。
posted on 2010-05-15 19:18 阅读(11) 所属分类:
昵称:
主页:
邮箱:(仅博主可见)
评论内容:
[使用Ctrl+Enter键快速提交评论]