Tuesday, February 19, 2008

About Object.dispose() in .NET 2.0

Dispose 方法一般会调用 GC.SuppressFinalize,因此在您使用 Dispose 之后,系统已经认为该对象已经释放,也不会尝试再次处置该对象。
您需要区分“对象资源被回收”和“对象被回收”两个不同的概念。
例如,f 是一个 Form 类型的对象,则在内存中,f 本身是一个托管指针,您如果使用了 Dispose 方法,那么 f 指向的 Form 对象中的资源将被释放,也就是说 f 现在指向的 Form 对象已经是无效的,甚至可能指向已经被别的对象占用的空间。
但是 f 自身仍然占有一个指针的空间(内容是原来的 Form 对象的地址),即使您使用 f = null,那么 f 只是自身的内容变成 null。f 对象本身仍然占用了一个指针的空间。
您永远无法控制一个托管指针本身的释放(也就是说您无法尝试通知垃圾回收器把 f 自身的空间处置)。垃圾回收器会自动地在 f 对象脱离生存期(如离开了声明的方法,或所在的类实例被处置)后回收该空间。
一般程序上所说的用 null 来释放空间是这样的:
object obj = XXXX;
obj = null;
这里如果把 obj 设置为 null,那么 obj 原来指向的那块空间可能就变成无人可访问了。这时候垃圾回收程序会回收那块空间,但是同样不会回收 obj 本身的空间。
将处置后对象设置为 null 一般来说在程序使用上有一定意义,因为您可以通过和 null 作比较方便的判断某个对象是否有效。如果不设置为 null,而您又处置了对象,那么如果您只是使用 obj != null 的方式来判断,之后对 obj 作某些操作,那么会引发 ObjectDisposedException。

===================================================

Object a = new Object();
此时 a 是一个指针,我们假设他占用 4 个字节(内存 1),而 Object 是一个对象,可能会占用 32 个字节(内存 2)。a 的 4 个字节的内容就是那个 Object 对象所在的位置。
a = null;
此处将 a 设置为 null,只是更改了 a 自身的值(把 a 自己的 4 个字节全部置 0),但是结果是使原来 a 指向的内容(内存 2)无法访问。垃圾回收器会看到这一情况,然后回收那个不可访问的对象。同时如果继续使用 a,那么代码运行的时候会检查到您试图获取位置 0 中对象的内容,而位置 0 表示空位置,因此会提示您 NullReferenceException。
如果用
a.Dispose();
那么程序会手动清理干净内存 2 中的一些资源(比如图像资源)。但是此时 a 的内容没有变化,指向的位置还是内存 2。这时就出现一个问题,a 无法在被使用(因为其中的内容已经被处置)。这时候如果您继续使用 a,那么系统不会检测到 NullReferenceException,但是因为实际上里面的内容已经被释放而用作他用,在尝试读取的时候系统会引发 ObjectDisposedException,也就是检测到您尝试读取已经释放掉的位置的内容。