.Net 垃圾回收机制原理(二)
上一篇文章介绍了.Net 垃圾回收的基本原理和垃圾回收执行Finalize方法的内部机制;这一篇我们看下弱引用对象,代,多线程垃圾回收,大对象处理以及和垃圾回收相关的性能计数器。
让我们从弱引用对象说起,弱引用对象可以减轻大对象带来的内存压力。
弱引用(Weak References)
当程序的根对象指向一个对象时,这个对象是可达的,垃圾回收器不能回收它,这称为对对象的强引用。和强引用相对的是弱引用,当一个对象上存在弱引用时,垃圾回收器可以回收此对象,但是也允许程序访问这个对象。这是怎么回事儿呢?请往下看。
如果一个对象上仅存在弱引用,并且垃圾回收器在运行,这个对象就会被回收,之后如果程序中要访问这个对象,访问就会失败。另一方面,要使用弱引用的对象,程序必须先对这个对象进行强引用,如果程序在垃圾回收器回收这个对象之前对对象进行了强引用,这样(有了强引用之后)垃圾回收器就不能回收此对象了。这有点绕,让我们用一段代码来说明一下:
void Method() {
//创建对象的强引用
Object o = new Object();
// 用一个短弱引用对象弱引用o.
WeakReference wr = new WeakReference(o);
o = null; // 移除对象的强引用
o = wr.Target; //尝试从弱引用对象中获得对象的强引用
if (o == null) {
// 如果对象为空说明对象已经被垃圾回收器回收掉了
} else {
// 如果垃圾回收器还没有回收此对象就可以继续使用对象了
}
}
为什么需要弱对象呢?因为,有一些数据创建起来很容易,但是却需要很多内存。例如:你有一个程序,这个程序需要访问用户硬盘上的所有文件夹和文件名;你可以在程序第一次需要这个数据时访问用户磁盘生成一次数据,数据生成之后你就可以访问内存中的数据来得到用户文件数据,而不是每次都去读磁盘获得数据,这样做可以提升程序的性能。
问题是这个数据可能相当大,需要相当大的内存。如果用户去操作程序的另外一部分功能了,这块相当大的内存就没有占用的必要了。你可以通过代码删除这些数据,但是如果用户马上切换到需要这块数据的功能上,你就必须重新从用户的磁盘上构建这个数据。弱引用为这种场景提供了一种简单有效的方案。
当用户切换到其他功能时,你可以为这个数据创建一个弱引用对象,并把对这个数据的强引用解除掉。这样如果程序占用的内存很低,垃圾回收操作就不会触发,弱引用对象就不会被回收掉;这样当程序需要使用这块数据时就可以通过一个强引用来获得数据,如果成功得到了对象引用,程序就没有必要再次读取用户的磁盘了。
WeakReference类型提供了两个构造函数:
WeakReference(object target);
WeakReference(object target, bool trackResurrection);
target参数显然就是弱引用要跟踪的对象了。trackResurrection参数表示当对象的Finalize方法执行之后是否还要跟踪这个对象。默认这个参数是false。有关对象的复活请参考这里。
方便起见,不跟踪复活对象的弱引用称为“短弱引用”;而要跟踪复活对象的的弱引用称为“长弱引用”。如果对象没有实现Finalize方法,那么长弱引用和短弱引用是完全一样的。强烈建议你尽量避免使用长弱引用。长弱引用允许你使用复活的对象,而复活对象的行为可能是不可以预知的。
一旦你使用WeakReference引用了一个对象,建议你将这个对象的所有强用都设置为null;如果强引用存在的话,垃圾回收器是永远都不可能回收弱引用指向的对象的。
当你要使用弱引用目标对象时,你必须为目标对象创建一个强引用,这很简单,只要用object a = weekRefer.Target;就可以了,然后你必须判断a是否为空,弱不为空才可以继续使用,弱为空就表示对象已经被垃圾回收器回收了,得通过其他方法重新获得此对象。
弱引用的内部实现
从前文中的描述中我们可以推断出弱引用对象肯定和一般对象的处理是不一样的。一般情况下如果一个对象引用了另一个对象就是强引用,垃圾回收器就不能回收被引用的对象,而WeakReference对象却不是这样子,它引用的对象是有可能被回收的。
要完全理解弱对象是如何工作的,我们还需要看一下托管堆。托管堆上有两个内部数据结构他们的唯一作用是管理弱引用:我们可以把它们称作长弱引用表和短弱引用表;这两个表存放托管堆上的弱引用目标对象指针。
程序运行之初,这两个表都是空的。当你创建一个WeakReference对象时,
相关新闻>>
- 发表评论
-
- 最新评论 更多>>