一般认为在应用程序执行期间,对象在任意时刻要么是活动的,即表示应用程序有指向对象的引用;要么对象是非活动的。当应用程序释放了指向对象的最后一个引用后,对象就从活动状态变为不活动状态了。
但是事实上,对象在“活动状态”和“非活动状态”之间还有一个第三种状态。如果对象处在这种状态下,应用程序可以访问对象,而CLR的垃圾收集器(GC)也可以随时释放对象。哎!这好象是很矛盾的啊?应用程序可以访问对象就说明还有引用指向对象啊,怎么可能会被GC随时释放啊?为了解决这个矛盾,就引入了这篇随笔所要说的弱引用(Weak Reference)了。当对象是以弱引用的方式引用的,那么对象就处于我们前面所说的第三种状态了,也就是说对象即可以被应用程序引用,也可以随时被GC释放。
那么.NET为什么要提供弱引用这个功能,这既增加了对象状态的复杂性,也加大了我们学习的难度,好象很不合算吗?其实弱引用还是很有用的,一般认为当满足下列条件时,我们就应该考虑使用弱引用:
- 对象稍后可能被使用,但不是很确定。(如果确定要使用,就应该用强引用)
- 如果需要,对象可以重新被构造出来(比如数据库构造)。
- 对象占相对比较大的内存(一般大于几KB以上)
好象上面所列的条件很理论很晦涩啊!其实在日常实践中,可以用一句话来概括:弱引用一般用来完成对象的缓存,以提高应用程序的性能。
既然使用弱引用有好处,并且我们又知道了应该在何时使用它,那到底应该如何使用引应用呢?说了这么多,怎么还不说弱引用的使用方法啊?别急,让我们来看一个例子:
protected void Page_Load(object sender, EventArgs e)
{
// 声明一个强类型引用对象
object obj = new object();
// 注意:这里声明了一个弱引用对象
System.WeakReference wobj = new WeakReference(
obj = null; // 删除强类型引用
obj = wobj;
// 下面的两个Response.Write语句都有可能被执行,
// 不过一般else下的Response.Write语句执行的机率高些,
// 因为GC只有在CLR监测到内存不足时,才进行垃圾收集
if (null == obj)
{
Response.Write("弱类型引用对象被清除");
}
else
{
Response.Write("弱类型引用对象可以使用");
}
}
怎么样,看了上面的例子,是不是发现弱引用的使用是很简单的一件事啊?其实WeakReference类提供了两个构造函数:{
// 声明一个强类型引用对象
object obj = new object();
// 注意:这里声明了一个弱引用对象
System.WeakReference wobj = new WeakReference(
obj = null; // 删除强类型引用
obj = wobj;
// 下面的两个Response.Write语句都有可能被执行,
// 不过一般else下的Response.Write语句执行的机率高些,
// 因为GC只有在CLR监测到内存不足时,才进行垃圾收集
if (null == obj)
{
Response.Write("弱类型引用对象被清除");
}
else
{
Response.Write("弱类型引用对象可以使用");
}
}
WeakReference(object target);
WeakReference(object target, bool trackResurrection);。
如果我们像上面的例子中那样使用第一个构造函数,那traceResurrection将会被置为默认值false。同样我们可以利用第二个构造函数将traceResurrection置为true。那这个参数究竟有什么用呢?其实当traceResurrection为true时,应用程序就可以在对象的Finalize()方法被调用后,但对象的内存还没有真正发生改变(即GC垃圾收集操作的堆碎片清理还没有清理到对象所使用的内存区域)的这段时间里继续访问对象。对于这种情况我们一般称之为“长弱引用”。对应的,当traceResurrection被置为false,我们称之为“短弱引用”。
最后说一句,虽然有时候长弱引用可以给我们带来一定的方便性,当我们最好避免使用它,因为长弱引用的维护十分困难。