看来题主的问题很多啊。
1、值类型、引用类型、对象的位置
值类型的对象可以存在于托管堆(譬如,当它被装箱后,或者被匿名委托捕获了)或者栈上。引用类型的对象只能存在于托管堆上,引用类型的引用可以存在于托管堆(譬如被匿名委托捕获了)或栈上。
2、栈上的释放
如果一个栈上存储的对象超过了作用域,则它被释放。
3、托管堆上的释放
托管堆上的对象可能永远都不被释放(直到程序结束),因为 GC 的意义是模拟一个内存无限的环境,只要当前内存还够用,GC 可以永远不执行垃圾清理任务。当内存不够的时候,托管堆上的不可达的东西将会有机会被调用 Finalizer 后,被释放占用的空间。
不可达可以如下定义:如果对象 A 的某个字段引用了对象 B,或者 A(作为一个引用类型变量)引用了 B,或者委托 A 捕获了 B,则建立 A 到 B 的边。如果从某个栈上的东西或静态成员到 X 有一条路,则 X 可达。不是可达的是不可达的。
4、关于题主提到了把引用设为 null
首先,如果 X 只有一个引用,你把它所引用的改为 null 之后,X 是不可达。但是不可达不一定会被垃圾收集,这时你可以使用 GC.Collect 来强迫收集,为了确保收集效果,你可以多次调用。
第二种情况,除了你“掌控”的 X 的某个引用外,还有别的地方在引用 X,那你很可能无法强迫 X 被收集,否则为了保持一致性,其他引用 X 的必须都被撤销对 X 的引用,但是这样做可以破坏其他地方的一致性,导致整个程序坏掉。对此,一种策略是在某些 applicable 的地方使用 WeakReference。
5、Dispose
Dispose 是为了释放非托管资源准备的。譬如有一个文件类 File,底层实现是保存一个 IntPtr 作为文件的句柄,因为 File 对象释放不确定,所以我们让 File 实现 IDisposable,Dispose 方法将会关闭这个文件并让这个 File 对象处在不可用状态。也就是说,Dispose 之后 File 对象不再持有这个文件的访问权,但是 File 对象本身不一定被 finalize 或收集。
如果你不信任GC,你就不应该用托管对象,进而C#这种BCL中99.9%的类型是引用类型的语言都不应该用。
如果你信任GC,就别管这些破事儿。
最后,如果你连int怎么回收都不知道,应当重新温习基础课程。