GC简述
垃圾回收机制简称GC,顾名思义,就是指对程序运行时产生的垃圾进行回收。这种操作提高了内存的使用效率,减少了人为操作内存带来的bug。在程序开发中,有着举足轻重的地位。
.NET中GC的原理
常见的GC算法有很多种,而在.NET中使用的是Mark Sweep(标记压缩算法)。
1阶段:在托管Heap(堆)中,先假设所有对象都可以回收,然后找出不能回收的对象(查找对象的引用,当对象被引用时,则视为不可回收),给不能回收的对象打上标记,最后回收heap中没有标记的对象。
2阶段:当对象回收之后,heap内存空间变得不连续,在heap中移动这些对象,使他们重新从heap基地址开始连续排列,类似于磁盘空间的碎片整理 。Heap内存经过回收、压缩之后,可以继续采用前面的heap内存分配方法,即仅用一个指针记录heap分配的起始地址就可以 。
主要处理步骤:将线程挂起=>确定roots=>创建reachable objectsgraph=>对象回收=>heap压缩=>指针修复
基于标记回收压缩的算法,.NET为了降低GC带来的性能损耗,采用了分代算法,将heap分为3个代龄区域,分别设置不同的大小,并且按照区域大小的反比来设置分代区域的GC的频率。
在程序中,生命周期比较长的对象,最后会进入最大的分代区域,在需要时,统一回收
.NET中的非托管资源的回收
.NET中自动管理的GC实际上就是我们通常认为的GC。通常情况下,GC自动运行进行的垃圾回收非常强大,自动的GC我们可以通过手调用GC.Collect方法来进行GC操作。
但是由于自动的GC有着两大缺点
1.GC无法释放非托管资源
2.GC并不是实时性的
在这里我们需要引入.NET中的资源分类,在.NET中,资源分为两类,处于heap中的资源,都属于托管资源,可以通过GC来释放,而处于栈中的资源,则是非托管资源,比如数据库连接,文件句柄等,这些需要通过其他方式来进行资源释放。
针对第一点,可以通过继承IDispose接口实现Dispose方法来进行手动的资源释放,
或者通过Finalize终结器来进行。
C#编译器在编译析构函数时,会隐式把析构函数的代码编译为等价于重写Finalize()方法的代码,从而确保执行父类的Finalize方法。
protected override void Finalize()
{
try
{
// Cleanup statements...
}
finally
{
base.Finalize();
}
}
需要注意的是,如果通过Dispose方法来进行手动释放内存,则需要调GC.SuppressFinalize();方法来避免GC自动调用Finalize方法,因为Finalize方法会造成一定的性能损耗。
并且由于负责调用 Finalize 的线程并不保证各个对象的 Finalize 的调用顺序,所以可能会带来微妙的依赖性问题。如果你在对象 a 的 Finalize 中引用了对象 b,而 a 和 b 两者都实现了 Finalize,那么如果 b 的 Finalize 先被调用的话,随后在调用 a 的 Finalize时就会出现问题,因为它引用了一个已经被释放的资源。因此,在 Finalize 方法中应该尽量避免引用其他实现了 Finalize 方法的对象。
引用博客链接:https://www.cnblogs.com/bruce1992/p/14072550.html