内存回收专家:.NET内存回收机制



[前言:].Net平台提供了许多新功能这些功能能够帮助员生产出更高效和稳定代码其中的就是垃圾回收器(GC)这篇文章将深入探讨这功能了解它是如何工作以及如何编写代码来更好地使用这.Net平台提供功能

  .Net中内存回收机制

  垃圾回收器是用来管理应用内存分配和释放在垃圾回收器出现以前员在使用内存时需要向系统申请内存空间有些语言例如VisualBasic可以自动完成向系统申请内存空间工作但是在诸如VisualC语言中要求员在代码中申请内存空间如果员在使用了内存的后忘了释放内存则会引起内存泄漏但是有了垃圾回收器员就不必关心内存中对象在离开生存期后是否被释放问题个应用在运行时候垃圾回收器设置了个托管堆托管堆和C语言中堆向类似但是员不需要从托管堆中释放对象并且在托管堆中对象存放是连续

  每次当开发人员使用运算符创建对象时运行库都从托管堆为该对象分配内存新创建对象被放在上次创建对象的后垃圾回收器保存了个指针该指针总是指向托管堆中最后个对象的后内存空间当新对象被产生时运行库就知道应该将新对象放在内存什么地方同时开发人员应该将相同类型对象放在例如当开发人员希望向数据库写入数据时侯首先需要创建个连接对象然后是Command对象最后是DataSet对象如果这些对象放在托管堆相邻区域内存取它们就非常快

  当垃圾回收器指针指向托管堆以外内存空间时就需要回收内存中垃圾了在这个过程中垃圾回收器首先假设在托管堆中所有对象都需要被回收然后它在托管堆中寻找被根对象引用对象(根对象就是全局静态或处于活动中局部变量以及寄存器指向对象)找到后将它们加入个有效对象列表中并在已经搜索过对象中寻找是否有对象被新加入有效对象引用直到垃圾回收器检查完所有对象后就有份根对象和根对象直接或间接引用了对象列表而其它没有在表中对象就被从内存中回收

  当对象被加入到托管堆中时如果它实现了finalize()思路方法垃圾回收器会在它终结列表(FinalizationList)中加入个指向该对象指针当该对象被回收时垃圾回收器会检查终结列表看是否需要对象finalize()思路方法如果有垃圾回收器将指向该对象指针加入个完成器队列中该完成器队列保存了那些准备finalize()思路方法对象到了这步对象还不是真正垃圾对象因此垃圾回收器还没有把他们从托管堆中回收

  当对象准备被终结时个垃圾回收器线程会在完成器队列中每个对象finalize()思路方法完成后线程将指针从完成器队列中移出这样垃圾回收器就知道在下次回收对象时可以清除被终结对象了从上面可以看到垃圾回收机制带来很大部分额外工作就是finalize()思路方法因此在实际编程中开发人员应该避免在类中实现finalize()思路方法

  对于finalize()思路方法个问题是开发人员不知道什么时候它将被它不像C析构在删除个对象时被为了解决这个问题在.Net中提供了个接口IDisposable微软建议在实现带有fianlize()思路方法时侯按照下面模式定义对象:


publicClass1:IDisposable
{
 publicClass1
 {
 }

 ~Class1
 {
  //垃圾回收器将该思路方法因此参数需要为false
  Dispose(false);
 }

 //该思路方法定义在IDisposable接口中
 publicvoidDispose
 {
  //该思路方法由该思路方法的后对象将被终结
  //我们不希望垃圾回收器再次终结对象因此需要从终结列表中去除该对象
  GC.SuppressFinalize(this);
  //是由该思路方法因此参数为true
  Dispose(true);
 }

 //所有和回收相关工作都由该思路方法完成
 privatevoidDispose(booldisposing)
 {
  lock(this)//避免产生线程
  {
   (disposing)
   {
    //需要员完成释放对象占用资源
   }

  //对象将被垃圾回收器终结在这里添加其它和清除对象相关代码
 }
}
}


  现在我们了解了垃圾回收器工作基本原理接下来让我们看看垃圾回收器内部是如何工作目前有很多种类型垃圾回收器微软实现了种生存期垃圾回收器(GenerationalGarbageCollector)生存期垃圾回收器将内存分为很多个托管堆个托管堆对应种生存期等级生存期垃圾回收器遵循着下面原则:

  新生成对象其生存期越短;而对象生成时间越长对象其生存期也就越长对于垃圾回收器来说回收部分对象总是比回收全部对象要快因此垃圾回收器对于那些生存期短对象回收频率要比生存期长对象回收频率高

  .Net中垃圾回收器中目前有 3个生存期等级:01和20、1、2等级对应托管堆化大小分别是256K2M和10M垃圾回收器在发现改变大小能够提高性能会改变托管堆大小例如当应用化了许多小对象,并且这些对象会被很快回收垃圾回收器就会将0等级托管堆变为128K并且提高回收频率如果情况相反垃圾回收器发现在0等级托管堆中不能回收很多空间时就会增加托管堆大小
在应用的前所有等级托管堆都是空当对象被时候他们会按照先后顺序被放入等级为0托管堆中在托管堆中对象存放是连续这样使得托管堆存取对象速度很快托管对不必对内存进行搜索垃圾回收器中保存了个指针指向托管堆中最后个对象的后内存空间中显示了个包含 4个对象0等级托管堆




包含 4个对象托管堆

  当0等级托管堆被对象填满后例如候化了新对象使0等级托管堆大小超过了256K垃圾回收器会检查托管堆中所有对象看是否有对象可以回收当开始回收操作时如前面提到垃圾回收器会找出根节点和根节点直接或间接引用了对象然后将这些对象转移到1等级托管堆中并将0等级托管堆指针移到最开始位置以清除所有对象同时垃圾回收器会压缩1等级托管堆以保证所有对象的间没有内存空隙当1等级托管堆满了的后会将对象转移到2等级托管堆

  例如在图的后垃圾回收器开始回收对象假定D对象将被回收同时创建了E和F对象这时候托管堆中对象如图 2所示


图 2回收对象后0等级和1等级托管堆

  然后创建了新对象G和H次触发了垃圾回收器对象E将被回收这时候托管堆中对象如图 3所示


 生存期垃圾回收器原则也有例外情况当对象大小超过84K时对象会被放入\"大对象区\"大对象区中对象不会被垃圾回收器回收也不会被压缩这样做是为了强制垃圾回收器只能回收小对象以提高性能

  控制垃圾回收器

  在.Net框架中提供了很多思路方法使开发人员能够直接控制垃圾回收器行为通过使用GC.Collect()或GC.Collect(GenerationNumber)开发人员可以强制垃圾回收器对所有等级托管堆进行回收操作在大多数情况下开发人员不需要干涉垃圾回收器行为但是有些情况下例如当进行了非常复杂操作后希望确认内存中垃圾对象已经被回收就可以使用上面思路方法个思路方法是GC.WaitForPendingFinalizers()它可以挂起当前线程直到处理完成器队列线程清空该队列为止

  使用垃圾回收器最好思路方法就是跟踪中定义对象不需要它们时候手动释放它们例如个对象中有串属性该属性会占用内存空间当该属性不再被使用时开发人员可以在中将其设定为null这样垃圾回收器就可以回收该串占用空间另外如果开发人员确定不再使用某个对象时需要同时确定没有其它对象引用该对象否则垃圾回收器不会回收该对象

  另外值得是finalize()思路方法应该在较短时间内完成这是垃圾回收器给finalize()思路方法限定了个时间如果finalize()思路方法在规定时间内还没有完成垃圾回收器会终止运行finalize()思路方法线程在下面这些情况下对象finalize()思路方法:

   0等级垃圾回收器已满

   了执行垃圾回收思路方法

   公共语言运行库正在卸载个应用

   公共语言运行库正在被卸载

Tags:  垃圾回收机制 内存回收

延伸阅读

最新评论

发表评论