GC中使用需要特殊清理的类型

21.3 使用需要特殊清理的类型

多数类型有内存就能正常工作,有的类型除了内存还需要本机资源,所以需要特殊清理。

CLR提供了终结(finalization)的机制,允许对象在判定为垃圾之后,但在对象内存回收之前执行一些代码。

终极基类System.Object定义了受保护的虚方法Finalize。垃圾回收器在判定对象是垃圾之后,会调用对象的Finalize方法(如果重写)。

特点

  • 被视为垃圾的对象在垃圾回收完毕之后才调用Finalize方法,因为Finalize方法可能要执行访问执行字段的代码,可终结对象在回收时必须存活,造成它被提升一代,使对象活的时间比较长
  • Finalize执行时间控制不了,只有在GC之后才运行Finalize,CLR不保证多个Finalize方法的调用顺序,所以在Finalize方法中不要访问定义了Finalize方法的其他类型的对象,那些对象可能已经终结了
  • CLR用一个特殊的、高优先级的专用线程调用FInalize方法来避免死锁

21.3.1使用包装了本机资源的类型

自定义包装了本机资源的SafeHandle派生类

类如果想允许使用者控制类所包装的本机资源的生存期,就必须实现IDisposable接口

调用Dispose来释放对象自身使用的资源

如果决定显示调用Dispose,建议将调用放到异常处理的finally块中,确保代码被执行

21.3.2一个有趣的依赖性问题

FileStream类型只支持字节的写入。写入字符和字符串可以使用StreamWriter

FileStream fs = new FileStream("Datafile.dat", FileMode.Create);
StreamWriter sw = new StreamWriter(fs);
sw.Write("Hi there");

sw.Dispose();

向一个StreamWriter对象写入时,它会将数据缓存在自己的内存缓冲区,缓冲区满时,StreamWriter对象将数据写入Stream对象。StreamWriter类型不支持终结,所以通过StreamWriter对象写入数据完毕后应显示调用Dispose,否则会造成数据丢失

21.3.3GC为本机资源提供的其他功能

GC提供两个静态方法AddMemoryPressure 和RemoveMemoryPressure

如果一个类要包装很大的本机资源,就应该使用这些方法提示垃圾回收器实际需要消耗多少内存,垃圾回收器会监视内部压力变化,压力变大时强制执行垃圾回收

如果一个类要包装数量有限制的本机资源,使用HandleCollector的实例来提示垃圾回收器要使用资源的多少个实例。

21.3.4 终结的内部构造原理

  • 应用程序创建对象时,如果对象的类型定义了Finalize方法,那么在该类型的实例构造器被调用之前,会将指向该对象的指针放到终结列表中。
  • 垃圾回收开始时,对象会被判定为垃圾。垃圾回收器扫描终结列表,查找对垃圾对象的引用,找到一个引用,会将该引用从终结列表中移除,并附加到freachable队列。
  • 一个特殊的高优先级的CLR线程专门调用Finalize方法,freachable队列中一旦有记录项出现,线程就被唤醒,将每一项从队列中移除并调用每个对象的Finalize方法。

当垃圾回收器将对象的引用从终结列表移至freachable队列时,我们称对象复活了,标记freachable对象时,递归标记对象中的引用类型的字段所引用的对象,所有这些对象必须复活以便在回收过程中存活。之后,垃圾回收器结束标记,移动可回收内存,将复活的对象提升至较老的一代。所以,可终结对象需要执行两次垃圾回收才能释放它占用的内存。

21.3.5手动监视和控制对象的生存期

GCHandleType枚举类型有四个标志

Weak:该标志允许监视对象的生存期。可检测垃圾回收器在什么时候判定该对象在应用程序代码中不可达。此时,对象的Finalize方法(如果有的话)可能已经执行,也可能未执行

WeakTrackResurrection:同Weak,但此时对象的Finalize方法(如果有的话)已经执行,对象的内存已被回收

Normal:该标志允许控制对象的生存周期。是告诉垃圾回收器,即使应用程序中没有根引用该对象,该对象也必须留在内存中。垃圾回收时,该对象的内存可以压缩。

Pinned:同Normal,垃圾回收时,该对象的内存不能压缩。

垃圾回收时,垃圾回收器的动作

  1. 垃圾回收器标记所有可达的对象,然后垃圾回收器扫描GC句柄表,所有Normal和Pinned对象被看成是根,同时递归标记这些对象字段引用的对象。
  2. 垃圾回收器扫描GC句柄表,查找所有Weak记录项,如果一个Weak记录项引用未标记的对象,该引用标识就是不可达对象(垃圾),该记录项的引用值更改为null
  3. 垃圾回收器扫描终结列表。在列表中,对未标记对象的引用标识的是不可达对象,这些引用移至freachable队列,这是对象会被标记,又变成可达了。
  4. 垃圾回收器扫描GC句柄表,查找所有WeakTrackResurrection记录项。如果一个WeakTrackResurrection记录项引用了未标记的对象,该引用标识的就是不可达对象,该记录项的引用值更改为null。
  5. 垃圾回收器对内存进行压缩。Pinned对象不会压缩,垃圾回收器会移动它周围的其他对象
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值