Programming .NET Components 2nd 学习笔记(四)

4.3. .NET Garbage Collection

.NET编程中,离开声明范围并不会销毁对象,但是不像COM.NET不对对象使用引用计数。与之替代的,.NET有一个复杂的垃圾回收机制,当它发现一个对象不再被使用时就会销毁该对象。为了达到该目的,.NET必须保持对代码中对象可访问路径的追踪。抽象来说,当JIT编译器编译IL代码时,它将更新一个包含原始应用程序起始点的列表,例如静态变量和方法,同时内部的.NET实体应该在应用程序运行时一直存活。每一个根会在一个树形图中形成一个顶端节点。.NET对托管堆分配的每个对象及每个对象与客户端的关系进行追踪。每当分配一个新对象时,.NET更新对象图并在对象图中添加一个新对象与创建它的对象的引用。同样的,.NET在客户端接收一个对象的引用和一个对象将另一个对象存为成员变量时更新对象图。JIT编译器同样会注入代码,当运行进入或离开一个范围时更新对象图。

 

垃圾回收器负责释放不使用的内存。当垃圾回收触发时(通常是在托管堆用尽时,但也会在代码明确要求垃圾回收时),垃圾回收器将对象图中的所有对象视作垃圾。接下来,垃圾回收器将会遍历每个对象图,从根处往下寻找可到达的对象。每当垃圾回收器访问对象时,将该对象标记为可到达。因为对象图代表着客户端与对象的关系,所以当垃圾回收器遍历完所有对象图后,它将知道哪些对象是可到达的,哪些对象是不可到达的。可到达的对象应该保持存活。而不可到达的对象将被视为垃圾,因此销毁它们不会造成损害。这种算法很好地解决了循环引用(例如,对象A引用了对象B,对象B引用了对象C,而对象C引用了对象A)。当垃圾回收器到达了一个已经标记为可到达的对象时,它将不会从该已标记的对象向下寻找其他可到达的对象。

 

接下来,垃圾回收器扫描托管堆并压缩托管堆,用可到达的对象去覆盖不可到达的对象,以此来处理不可到达的对象。垃圾回收器将可到达的对象向堆的底端移动,覆盖垃圾,因此会释放更多的空间来分配新对象。所有不可到达的对象将会从对象图中清除掉。整个过程如下图所示:

 

然而,通过将所有可到达对象向下移动来压缩堆意味着客户端拥有的对象引用现在指向一个错误的地址(例如图中对象810)。垃圾回收器将会更新所有移动的对象在客户端的引用来修复这个错误。

 

垃圾回收器面临的另一个问题是它必须保证应用程序代码不会去修改对象图的结构,或者清理工作时的堆状态。唯一的安全方法是在垃圾回收时挂起应用程序的所有线程。那么.NET又是如何知道什么时候挂起线程是安全的呢?这个线程可能正处于分配内存中,或修改数据结构,例如向一个链表添加元素。为了解决这个问题,JIT编译器在代码中加入安全点,这些点在执行轨道中能安全挂起线程(例如方法调用的返回时)。事实上,垃圾回收器通过在安全点插入一个不同的返回地址来拦截线程:这个地址包含了一个挂起该线程的调用。当垃圾回收完毕,.NET使挂起的线程回到它原来的返回地址接着执行。

 

警告:垃圾回收通常在托管堆耗尽时触发,但应用程序停止也会触发垃圾回收。

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值