.Net垃圾回收机制

.net中的资源有两种,托管资源和非托管资源。下面来尝试分析下这两种类型资源的回收。

1.非托管资源回收

两种方式:Dispose,Finalize

1.1Dispose

Dispose方法,继承IDisposable接口,也就会自动调用Dispose方法。就可以在using里创建对象了。

 class Student : IDisposable
    {
        public void Dispose()
        {
            throw new NotImplementedException();
        }
    }

调用的时候:

 Student stu = new Student();
            stu.Dispose();

或者:

using (Student stu1 = new Student())
            { 
                
            }

using的好处就是,在大括号范围之外,对象就自动释放,无需显式调用dispose方法。

1.2Finalize()方法(不推荐)

class Student : IDisposable
    {
        ~Student() { }
    }

~Student()是析构函数,会隐式调用Finalize方法,即clr将析构函数隐式转换为:

protected override void Finalize()  
{  
    try  
    {  
       // Cleaning up .  
    }  
    finally  
    {  
       base.Finalize();  
    }
}

引用一下网上有大神的博客:

在.NET中应该尽可能的少用析构函数释放资源,MSDN2上有这样一段话:
  实现 Finalize 方法或析构函数对性能可能会有负面影响,因此应避免不必要地使用它们。用 Finalize 方法回收对象使用的内存需要至少两次垃圾回收。当垃圾回收器执行回收时,它只回收没有终结器的不可访问对象的内存。这时,它不能回收具有终结器的不可访问对象。它改为将这些对象的项从终止队列中移除并将它们放置在标为准备终止的对象列表中。该列表中的项指向托管堆中准备被调用其终止代码的对象。垃圾回收器为此列表中的对象调用 Finalize 方法,然后,将这些项从列表中移除。后来的垃圾回收将确定终止的对象确实是垃圾,因为标为准备终止对象的列表中的项不再指向它们。在后来的垃圾回收中,实际上回收了对象的内存。

  所以有析构函数的对象,需要两次,第一次调用析构函数,第二次删除对象。而且在析构函数中包含大量的释放资源代码,会降低垃圾回收器的工作效率,影响性能。所以对于包含非托管资源的对象,最好及时的调用Dispose()方法来回收资源,而不是依赖垃圾回收器。

2.托管资源由GC回收

先弄清楚一个概念:

CLR使用了“代”概念来优化垃圾回收器,代是垃圾回收机制使用的一个逻辑技术,也是一种算法,它把托管堆中的内存分为3个代

(截止到目前.NET Framework4.0有3个代:0、1、2)。

在激活一个进程时,CLR会先保留一块连续的内存,在主线程启动过程中,可能会初始化一系列对象,CLR先计算对象大小及其开销所占用的字节数,接着会在连续的内存块中为这些对象分配内存,这些对象被配置在第0代内存,在构造第0代内存的时候会分配一个默认大小的内存,随着程序的运行,可能会初始化更多的对象,CLR发现第0代内存不能装载更多的新生对象,此时CLR会启动垃圾回收器对第0代内存进行回收,不再使用的对象所占用的内存会被释放,接着把0代对象提升为第1代,然后把新生对象配置在第0代内存区中。CLR使用了3个阶段的代,每次新分配的对象都会被配置在第0代内存中,最老的对象在第2代内存中,每次为新对象分配内存时,都可能会进行垃圾回收以释放内存。

垃圾回收过程:

1.查找根

clr验证线程栈上行查找根(静态字段、方法参数、局部变量、寄存器中的对象等等)。

2.标记根

标记查找到的所有跟,如果跟引用了对象A,则将A标记,如果对象A引用了对象B,则将B标记。如果多个对象都引用了一个对象C,clr会判断已经被标记过的,则不会标记。

3.回收

有标记的对象称为可达对象,没有标记的对象称为不可达对象。不可达对象就是将被回收的垃圾。clr将回收这一部分不可达对象。

4.搬迁、压缩

垃圾回收后,占用的内存被释放,clr将可达对象搬迁到这里以压缩堆。

5.修改引用

搬迁可达对象后,所有指向这些可达对象的引用将失效,GC会重新遍历程序的所有跟,来修改引用。

如果各个线程正在执行,很可能导致变量引用到无效的对象地址,所以整个进程的正在执行托管代码的线程是被挂起的。

6.线程挂起算法

挂起时,CLR会记录每个线程的指令指针以确定线程当前执行到哪里以便将来在垃圾回收结束后进行恢复。

如果一个线程的指令指针恰好到达了一个安全点,则可以挂起该线程,否则CLR会尝试劫持该线程,如果还未到达安全点,则等待几百毫秒后CLR会尝试再一次劫持该线程,有可能经过多次尝试,最终挂起该线程,当当前进程的所有执行托管代码的线程都挂起后,垃圾回收器就可以开始工作了。


博客园有大神的文章写得非常非常详细:

http://www.cnblogs.com/solan/archive/2012/08/24/CSharp11.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值