代码改变世界

.net中GC原理浅析

2010-09-14 01:17  轩脉刃  阅读(7110)  评论(7编辑  收藏  举报

GC(Garbage Collection)垃圾收集,指的是在.net中垃圾内存收集的机制。

首先要了解的几点:

  1. 在.net中,托管代码的内存管理是自动的,由GC进行管理。但是对于非托管代码,.net就无法自动管理了。
  2. CLR运行时候,内存分为“托管堆”和“栈”两个部分。其中,栈是用于存储值类型的数据,托管堆是用于存储引用类型的变量。其中托管堆是GC处理的内存部分。
  3. 进程中每个线程都有自己的堆栈。

 

对于托管代码的GC原理解读:

垃圾判定:

回收垃圾首先要知道什么是垃圾,一个变量如果在其生存期内的某一时刻已经不再被引用,那么,这个对象就有可能成为垃圾。

public static void Main()
              {
                 string sGarbage = "I'm here";
           //下面的代码没有再引用s,它已经成为垃圾对象---当然,这样的代码本身也是垃圾;
            //此时如果执行垃圾收集,则sGarbage可能已经魂归西天
                  Console.WriteLine("Main() is end");
              }

 

 

对象代龄:

GC认为,越晚创建的对象越短命,所以,其引入一个“代龄”的概念来划分对象生存级别

这个代龄划分机制简要来说是一代新人换旧人:

 内存代龄

CLR初始化后的第一批被创建的对象被列为0代对象。CLR会为0代对象设定一个容量限制,当创建的对象大小超过这个设定的容量上限时,GC就会开始工作,工作的范围是0代对象所处的内存区域,然后开始搜寻垃圾对象,并释放内存。当GC工作结束后,幸存的对象将被列为第1代对象而保留在第1代对象的区域内。此后新创建的对象将被列为新的一批0代对象,直到0代的内存区域再次被填满,然后会针对0代对象区域进行新一轮的垃圾收集,之后这些0代对象又会列为第1代对象,并入第1代区域内。第1代区域起初也会被设上一个容量限制值,等到第1代对象大小超过了这个限制之后,GC就会扩大战场,对第1代区域也做一次垃圾收集,之后,又一次幸存下来的对象将会提升一个代龄,成为第2代对象。


    可见,有一些对象虽然符合垃圾的所有条件,但它们如果是第1代(甚至是第2代老臣)对象,并且第1代的分配量还小于被设定的限制值时,这些垃圾对象就不会被GC发现,并且可以继续存活下去。

 

 

对于非托管代码,GC不能自动收集垃圾,需要的方法有两种:1,重写让GC自动调用的Finalize方法。 2,实现IDispose提供给我们显示调用的方法Dispose()

  1. Finalize

~ClassName() {//释放你的非托管资源}

Finalize是由GC负责调用,是一种自动释放的方式。

问:为什么说实现了Finalize方法的对象必需等两次GC才能被完全释放?

 有Finalize函数的两次GC回收

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

        

         第一次的GC做的事情是:1将有终结器的对象放到准备终结列表中,并执行Finalize方法。2 实际删除对象内存。

  1. Dispose

Dispose是提供给我们显示调用的方法。由于对Dispose的实现很容易出现问题,所以在一些书籍上(如《Effective C#》和《Applied Microsoft.Net Framework Programming》)给出了一个特定的实现模式:

class DisposePattern :IDisposable
    {
        private System.IO.FileStream fs = new System.IO.FileStream("test.txt", System.IO.FileMode.Create);

        ~DisposePattern()
        {
            Dispose(false);
        }       

        IDisposable Members#region IDisposable Members

        public void Dispose()
        {
            //告诉GC不需要再调用Finalize方法,
            //因为资源已经被显示清理
            GC.SupdivssFinalize(this);

            Dispose(true);
        }

        #endregion
               
        protected virtual void Dispose(bool disposing)
        {
            //由于Dispose方法可能被多线程调用,
            //所以加锁以确保线程安全
            lock (this)
            {
                if (disposing)
                {
                    //说明对象的Finalize方法并没有被执行,
                    //在这里可以安全的引用其他实现了Finalize方法的对象
                }

                if (fs != null)
                {
                    fs.Dispose();
                    fs = null; //标识资源已经清理,避免多次释放
                }
            }
        }
    }

注: 关键字using()中包含的变量就需要实现了IDispose借口,当出了using的范围的时候会自动使用Dispose方法。

 

作者:Nick Ye(yjf512)
出处:(http://www.cnblogs.com/yjf512/
版权声明:本文的版权归作者与博客园共有。转载时须注明本文的详细链接,否则作者将保留追究其法律责任。

 

参考文档:

http://hi.baidu.com/skytaxy/blog/item/26fb53956eee276655fb96b0.html

http://hi.baidu.com/luoyuonline/blog/item/7f72e3c91a4d221f7e3e6fea.html

http://www.legalsoft.com.cn/docs/894.html

http://www.ittang.com/2007/1202/2807.html

http://msdn.microsoft.com/zh-cn/library/0s71x931(VS.80).aspx