C#.Net 垃圾回收机制GC详解

原文:http://blog.csdn.net/aoshilang2249/article/details/38581101


笔记:

GC回收资源,不是直接删除,而是先将资源重定位到回收位置,然后再删除,这样回收不会生成很多文件碎片。

最近没用的资源放0level里,GC 最先回收这部分,当0level回收不了的放到level1,当回收level0后,内存依然吃紧,则回收level1的资源,level1回收后仍留下的放入level2.


C#.Net 垃圾回收机制GC详解

标签: c#.net内存管理垃圾回收
  964人阅读  评论(0)  收藏  举报
  分类:

目录(?)[+]

【优点】


1.提高软件系统的内聚。
2.降低编程复杂度,使程序员不必分散精力去处理析构。
3.不妨碍设计师进行系统抽象。
4.减少由于内存运用不当产生的Bug。
5.成功的将内存管理工作从程序的编写时,脱离至运行时,使不可预估的管理漏洞变为可预估的。

【算法工作原理】


垃圾收集器的本质,就是跟踪所有被引用到的对象,整理不再被引用的对象,回收相应的内存。
这听起来类似于一种叫做“引用计数(Reference Counting)”的算法,然而这种算法需要遍历所有对象,并维护它们的引
用情况,所以效率较低些,并且在出现“环引用”时很容易造成内存泄露。所以.Net中采用了一种叫做“标记与清除
(Mark Sweep)”算法来完成上述任务。

标记与清除”算法,顾名思义,这种算法有两个本领:

“标记”本领——垃圾的识别:从应用程序的root出发,利用相互引用关系,遍历其在Heap上动态分配的所有对
象,没有被引用的对象不被标记,即成为垃圾存活的对象被标记,即维护成了一张“根-对象可达图”。其实,
CLR会把对象关系看做“树图”,无疑,了解数据结构的都知道,有了“树图”的概念,会加快遍历对象的速度。

检测并标记对象引用,是一件很有意思的事情,有很多方法可以做到,但是只有一种是效率最优的,.Net中是利
用栈来完成的,在不断的入栈与出栈中完成检测:先在树图中选择一个需要检测的对象,将该对象的所有引用压栈,
如此反复直到栈变空为止。栈变空意味着已经遍历了这个局部根(或者说是树图中的节点)能够到达的所有对象。树图
节点范围包括局部变量(实际上局部变量会很快被回收,因为它的作用域很明显、很好控制)、寄存器、静态变量,这
些元素都要重复这个操作。一旦完成,便逐个对象地检查内存,没有标记的对象变成了垃圾。

“清除”本领——回收内存:启用Compact算法,对内存中存活的对象进行移动,修改它们的指针,使之在内存
中连续,这样空闲的内存也就连续了,这就解决了内存碎片问题,当再次为新对象分配内存时,CLR不必在充满碎片
的内存中寻找适合新对象的内存空间,所以分配速度会大大提高。但是大对象(large object heap)除外,GC不会移
动一个内存中巨无霸,因为它知道现在的CPU不便宜。通常,大对象具有很长的生存期,当一个大对象在.NET托管
堆中产生时,它被分配在堆的一个特殊部分中,移动大对象所带来的开销超过了整理这部分堆所能提高的性能。

Compact算法除了会提高再次分配内存的速度,如果新分配的对象在堆中位置很紧凑的话,高速缓存的性能将会
得到提高,因为一起分配的对象经常被一起使用(程序的局部性原理),所以为程序提供一段连续空白的内存空间是很
重要的。

【代龄】


代龄就是对Heap中的对象按照存在时间长短进行分代,最短的分在第0代,最长的分在第2代,第2代中的对象往
往是比较大的。Generation的层级与FrameWork版本有关,可以通过调用GC.MaxGeneration得知。

通常,GC会优先收集那些最近分配的对象(第0代),这与操作系统经典内存换页算法“最近最少使用”算法如出一
辙。但是,这并不代表GC只收集最近分配的对象,通常,.Net GC将堆空间按对象的生存期长短分成3代:新分配的
对象在第0代(0代空间最大长度通常为256K),按地址顺序分配,它们通常是一些局部变量;第1代(1代空间最大长度
通常为2 MB)是经过0代垃圾收集后仍然驻留在内存中的对象,它们通常是一些如表单,按钮等对象;第2代是经历过
几次垃圾收集后仍然驻留在内存中的对象,它们通常是一些应用程序对象。

当内存吃紧时(例如0代对象充满),GC便被调入执行引擎——也就是CLR——开始对第0代的空间进行标记与压
缩工作、回收工作,这通常小于1毫秒。如果回收后内存依然吃紧,那么GC会继续回收第1代(回收操作通常小于10毫
秒)、第2代,当然GC有时并不是按照第0、1、2代的顺序收集垃圾的,这取决于运行时的情况,或是手动调用
GC.Collect(i)指定回收的代。当对第2代回收后任然无法获得足够的内存,那么系统就会抛出OutOfMemoryException
异常

当经过几次GC过后,0代中的某个对象仍然存在,那么它将被移动到第1代。同理,第1、2代也按同样的逻辑运行。GC Heap中代的数量与容量,都是可变的。

【使用方式】


Dispose可用于释放所有资源,包括托管的和非托管的,需要自己实现。

大多数的非托管资源都要求手动释放,实现IDispose接口的Dispose方法是最好的;而且C#中用到的using语句
快,也是在离开语句块时自动调用Dispose方法。

这里需要注意的是,如果基类实现了IDispose接口,那么它的派生类也必须实现自己的IDispose,并在其
Dispose方法中调用基类中base.Dispose方法。只有这样的才能保证当你使用派生类实例后,释放资源时,连同基类
中的非托管资源一起释放掉。

SuppressFinalize用于那些即有析构函数释放资源,又实现了Dispose()方法释放资源的情况下将GC.SuppressFin
alize(this)添加至Dispose()方法中,以确保程序员调用Dispose()后,GC就不必再次垃圾回收了

[cpp]  view plain  copy
 print ?
  1. public class UnManagedResRelease : IDisposable  
  2.         {  
  3.             private bool _alreadyDisposed = false//保证资源只用释放一次  
  4.   
  5.             ~UnManagedResRelease()  
  6.             {  
  7.                 Dispose(false);  
  8.             }  
  9.   
  10.             /// <summary>  
  11.             /// 判断释放资源的类别(托管和非托管)  
  12.             /// </summary>  
  13.             /// <param name="isDisposing">是否是托管资源</param>  
  14.             protected virtual void Dispose(bool isDisposing)  
  15.             {  
  16.                 if (_alreadyDisposed)  
  17.                 {  
  18.                     return;  
  19.                 }  
  20.   
  21.                 if (isDisposing)  
  22.                 {  
  23.                     //释放托管资源...  
  24.                 }  
  25.   
  26.                 //释放非托管资源...  
  27.   
  28.                 _alreadyDisposed = true;  
  29.             }  
  30.   
  31.             public void Dispose()  
  32.             {  
  33.                 Dispose(true);  
  34.   
  35.                 //阻止GC把该对象放入终结器队列  
  36.                 GC.SuppressFinalize(this);  
  37.             }  
  38.         }  



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值