简单了解C#的GC机制

#.NET 共 1,310 字 约 5 分钟

C#编译

先将源码编译为中间代码MSIL(Microsoft Intermediate Language),再由.net中的CLR(公共语言运行时,一种运行时环境,运行代码并提供使开发过程更轻松的服务)将中间代码编译成机器码。
Java是先编译后解释,而C#是两次编译。

资源

资源就是程序中可利用的数据,例如字符串、图片等。
访问资源的步骤:

  • 分配内存
  • 初始化内存
  • 使用资源
  • 销毁资源

托管资源&非托管资源

  • 托管资源指的是.NET可以自动进行回收的资源,主要是托管堆上分配的内存资源。
  • 非托管资源是.NET不知道如何回收的资源,例如文件、窗口、网络连接等。

垃圾?

什么是垃圾?
啥都不会就是辣鸡
.NET两大类型,值类型和引用类型。值类型分配在栈上,不需要GC回收;引用类型分配在堆上,需要GC回收。
一个引用类型对象或者其包含的子对象没有任何引用是有效的,则系统认定为垃圾。

内存中的垃圾分为两种。

  • 需要调用对象的析构函数:回收需要两步,先调用对象的析构函数,再回收内存。需要两次轮循完成。
  • 不需要调用的

GC(Garbage Collector)机制

官方文档的注解是

plaintext
UTF-8|3 Lines|
The garbage collector is a common language runtime 
component that controls the allocation and release
of managed memory

即CLR的一个控制内存的分配和释放的组件。

plaintext
UTF-8|2 Lines|
Garbage collection happens automatically when a request 
for memory can't be satisfied using available free memory

GC发生的时机,就是相应的堆达到了阈值,因为堆也有大小限制,并不是无限的。

为了优化性能,使用了generation“代”的概念。将托管堆分为三代:第0代、第1代和第2代。垃圾回收算法基于几个普遍原理:

  • 压缩托管堆的一部分内存要比压缩整个托管堆速度快
  • 较新的对象生存期较短,较旧的对象生存期较长
  • 较新的对象趋向于相互关联,大致同时由应用程序访问

关于代数,官方文档的解释。

plaintext
UTF-8|13 Lines|
- 第 0 代。   
这是最年轻的代,其中包含短生存期对象。  
短生存期对象的一个示例是临时变量。垃圾回收最常发生在此代中。  
新分配的对象构成新一代对象,并隐式地成为第 0 代集合。  
大多数对象通过第 0 代中的垃圾回收进行回收,不会保留到下一代。
如果应用程序在第 0 代托管堆已满时尝试创建新对象,垃圾回收器将执行收集,以尝试为该对象释放地址空间。 垃圾回收器从检查第 0 级托管堆中的对象(而不是托管堆中的所有对象)开始执行回收。 单独回收第 0 代托管堆通常可以回收足够的内存,这样,应用程序便可以继续创建新对象。
- 第 1 代。  
这一代包含短生存期对象并用作短生存期对象和长生存期对象之间的缓冲区。  
垃圾回收器执行第 0 代托管堆的回收后,会压缩可访问对象的内存,并将其升级到第 1 代。 因为未被回收的对象往往具有较长的生存期,所以将它们升级至更高的级别很有意义。 垃圾回收器在每次执行第 0 代托管堆的回收时,不必重新检查第 1 代和第 2 代托管堆中的对象。
如果第 0 代托管堆的回收没有回收足够的内存供应用程序创建新对象,垃圾回收器就会先执行第 1 代托管堆的回收,然后再执行第 2 代托管堆的回收。 第 1 级托管堆中未被回收的对象将会升级至第 2 级托管堆。
- 第 2 代。  
这一代包含长生存期对象。 长生存期对象的一个示例是服务器应用程序中的一个包含在进程期间处于活动状态的静态数据的对象。
第 2 代托管堆中未被回收的对象会继续保留在第 2 代托管堆中,直到在将来的回收中确定它们无法访问为止。

当条件得到满足时,垃圾回收将在特定代上发生。 回收某个代意味着回收此代中的对象及其所有更年轻的代。 第 2 代垃圾回收也称为完整垃圾回收,因为它回收所有代中的对象(即托管堆中的所有对象)。
垃圾回收中未回收的对象也称为幸存者,并会被提升到下一代。

清理非托管资源

  • 实现清理模式。 提供 IDisposable.Dispose 实现以启用非托管资源的确定性释放。 当不再需要此对象(或其使用的资源)时,类型使用者可调用 Dispose。 Dispose 方法立即释放非托管资源。
  • 使用安全句柄包装非托管资源。 这是推荐采用的方法。 安全句柄派生自 System.Runtime.InteropServices.SafeHandle 抽象类,并包含可靠的 Finalize 方法。 在使用安全句柄时,只需实现 IDisposable 接口并在 Dispose 实现中调用安全句柄的 IDisposable.Dispose 方法。 如果未调用安全句柄的 Dispose 方法,则垃圾回收器将自动调用安全句柄的终结器。
  • 重写 Object.Finalize 方法。 当类型使用者无法调用 IDisposable.Dispose 以确定性地释放非托管资源时,终止会启用对非托管资源的非确定性释放。 通过重写 Object.Finalize 方法来定义终结器。

测试实践

待建设

小结

本文源自对using语句的学习,在搜索资料的过程中对GC机制有一个初步的简单了解,但是没有更深入的实践和理解,留作之后用到时再进行学习。

参考

浅谈c#垃圾回收机制(GC)

微软官方文档