谈谈.net对象生命周期(垃圾回收)(3)

  到这里,通过对应用程序根的作用的理解,我们知道了如何知道一个对象是“不再需要”的。通俗点来说就是,这个对象在应用程序中已经无需被访问了,成为了一座“孤岛”,自然也就不再需要它了。

  (为了让c++程序员能更加理解. net垃圾回收的奥妙,c#程序员继续滔滔不绝…)

理解对象的代 — 垃圾回收过程的优化

  在尝试找到不可达的对象时,CLR并不是检查托管堆上的每个对象。很明显,这样做会消耗大量时间,尤其在大型(例如现实中)程序中。

  为了帮助优化这个过程,堆上的每个对象被分配到一个特殊的"代”。代这个概念背后的想法很简单:对象在堆上存活的时间越长,接下来它继续存在的可能性也就越大,即较旧的对象生存期长,较新的对象生存期短。例如,实现Main()的对象一直在内存中,直到程序结束。相反,最近才被放到堆中的对象(例如在一个函数范围里分配的对象)很可能很快就不可达。

  在堆上的每个对象属于以下的某一个代:

  Generation 0: 标识一个最近分配的还没有被标记为回收的对象

  Generation 1: 标识一个经历了一次垃圾回收而存活下来的对象(例如,他被标记为回收,但由于堆空间够用而没有被清除掉)

  Generation 2:标识一个经历了不止一轮垃圾回收而存活下来的对象。

  垃圾回收器首先会检查generation 0的所有对象。如果标记并清理这些对象(译者注:因为新对象的生存期往往较短,并且期望在执行回收时,应用程序不再使用第 0 级托管堆中的许多对象)后产生了足够使用的内存空间,任何存活下来的对象就被提升到Generation 1。为了理解一个对象的代如何影响回收的过程,可以查看下图。下图解释了generation 0中一次垃圾回收后,存活的对象被提升的过程。  

谈谈.net对象生命周期(垃圾回收)

(generation 0 中的存活对象被提升到generation 1)

  如果所有的generation 0对象都被检查了,但是产生的内存空间仍然不够用,就检查一遍generation 1中的所有对象的可达性并回收。存活下来的generation 1对象被提升到generation 2。如果垃圾回收器仍然需要额外的内存,generation 2的对象就经历检查并被回收。此时,如果一个generation 2的对象存活下来,它仍然是一个generation 2的对象。

  其实通过对象的代的设计是想达到这么一个效果:新对象(比如局部变量)会被很快回收,而老一些的对象(如一个应用程序对象)不会被经常骚扰。

  说到底,对象代的设计就是为了优化垃圾回收的过程。  

  “我还有最后一个问题”,c++程序员按耐不住心里一直的疑惑,说到:“你说了这么多都是再讲托管资源,难道.net中就没有非托管资源吗?. net又是怎么对非托管资源进行资源释放的呢?”。

  "这个问题问的好!",c#程序员大笑,于是接着又开始解惑(吹B)…

构建可终结对象 —非托管资源处理第一式

  以一名c#开发者的直觉告诉你,大多数的c#类都不需要显式的清理逻辑。原因很简单:如果类型使用了其他托管对象,一切都最终会被垃圾回收。

  问:那在什么时候需要显式地清理呢?

  答案是:在你使用非托管资源时(例如原始的操作系统文件句柄、原始的非托管数据连接或其他非托管资源),才可能需要设计一个在用完后清理自身垃圾的类。

  比如说下面这个类:

//数据库上下文类 public class SqlDbContext { //...(其他被引用的对象实例) //类中包含的非托管资源(需要调用 Dispose()函数进行资源的释放) SqlConnection sqlConnection = new SqlConnection("..."); }

  现在问题来了,我们要在适当的时机调用数据库连接类对象释放资源的方法(SqlConnection类对象使用完后需要调用Dispose()方法释放资源)。这个适当的时机当然就是对象在被CLR进行垃圾回收的过程中,所以问题又来到了,有没有一个方法是在这个时机被调用,而且是可以被扩展的呢?

  是的,我们可以利用. NET的基类System.Object中定义的名为Finalize()的虚方法,也叫作终结器方法,它是这样的:

  

谈谈.net对象生命周期(垃圾回收)

  看到这当然会很奇怪,不是说有Finalize()方法,在哪,逗我?莫惊讶,其实这里的 ~Object() 就是Finalize(),只是一个语法糖罢了。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdsydg.html