代码改变世界

《你必须知道的.NET》读后小结(2)

2012-03-13 21:30  Oliver_Zhao  阅读(145)  评论(0)    收藏  举报

  首先,这个文章是对《你必须知道的.Net》的读后小结,还有出于自己的方便才写的,以后还会对其他章节进行总结。有一定.net基础的人,可以瞄一下。.Net基础很好的或者看过《你必须知道的.Net》书的人,可以回顾一下。如果文章有什么错误,感谢大家指出。2012-03-12

《你必须知道的.NET》读后小结(2)

内存管理:

  CLR引入垃圾收集器(GC,Garbage Collection)来负责执行内存清理工作,GC通过对托管堆的管理,能有效解决程序中类似于内存泄漏、访问不可达对象等问题。但是垃圾回收并不能解决所有资源的清理,对非托管资源(数据库链接、文件句柄、COM对象等等),任然需要开发者自行清理,.Net又是如何处理的呢?

.Net的自动内存管理,主要包括:对象创建时的内存分配;垃圾回收;非托管资源释放。

    class Program
    {
        static void Main(string[] args)
        {
            FileStream fs = new FileStream(@"c:\temp.txt", FileMode.Create);
            try
            {
                byte[] txts = new UTF8Encoding(true).GetBytes("Hello, world.");
                fs.Write(txts, 0, txts.Length);
            }
            finally
            {
                if (fs != null)
                {
                    fs.Close();
                }
            }
        }
    }

我们关注的是FileStream类型对象从创建到消亡的整个过程:

  • 对象的创建及内存分配。通过new关键字执行对象并分配内存,除了这种创建方式,.Net还提供了其他的对象创建方式与内存分配。
  • 对象初始化。通过调用构造函数,完成对象成员初始化。
  • 对象的应用和操作。
  • 资源清理。应用完成后,必须对对象访问的资源进行清理。
  • 垃圾回收。

对象的创建:

内存分配:

  对于值类型来说,一般创建在线程的堆栈上。但并非所有的值类型都创建在线程栈上,例如作为类的字段时,值类型作为实例成员的一部分被创建托管堆上。在装箱时,值类型字段会被拷贝到托管堆上。由于堆栈才用从高位到低位存储,所以出栈时,从低位到高位释放。《CLR via C#》中有详细说明,因为在堆栈上创建一个活动记录包含了很多东西,如参数、返回值地址和局部变量等。

  引用类型的实例分配于托管堆上,而线程栈确是对象生命周期开始的地方。当我们声明一个变量的时候,然后为它实例化一个对象,初始化时,其实在内存中,我们不仅仅存放了类的成员变量,同时系统还为每个类,另外添加了一些东西,TypeHandle,SyncBlockIndex和NextObjPtr。当我们使用new关键字时,CLR按照继承层次搜索,初始化其所有父类的字段。计算实例对象所占总数,当然还要加上附加成员所需的字节总数。然后,CLR在当前AppDomain对应的托管堆上搜索,找到未使用的连续空间,并为其分配该内存地址。

静态字段的内存分配和释放。静态字段保存在方法表中,位于方法表的槽数组后,其生命周期为从创建到AppDomain卸载。无论类型创建了多少个,静态字段在内存中只有一份。静态字段只能由静态构造函数进行初始化,静态构造函数确保在任何对象创建前,或者在任何静态字段或方法被引用前执行。

垃圾回收:

托管资源清理

  • 一个对象成为“垃圾”就表示该对象不被任何其他对象所引用。GC必须采用一定算法在托管堆中遍历所有对象,形成一个可达对象图,而不可达的对象将成为被释放的垃圾对象等待收集。

  • 垃圾收集齐周期性的执行内存清理工作,一般在以下情况出现时垃圾收集器将会启动:1)第0代对象充满时。2)调用GC.Collect方法。3)Windows报告内存不足,CLR强制执行垃圾回收。4)CLR卸载AppDomain时,GC回收所有代对象。5)其他情况。

  • GC垃圾回收之后,堆上将出现多给被收集对象的“空洞”,为避免托管堆的内存碎片,会重新分配内存,压缩托管堆,GC找到一块较大的连续区域,将未被收集的对象移到这个连续区域,同时还要对这些对象重定位,修改应用程序的根以及发生引用的对象指针,来更新复制后的对象位置。

非托管资源清理

  • Finalize方法。通过对自定义类型实现一个Finalize方法来释放非托管资源,而终止化操作在对象的内存回收之前通过调用Finalize方法来释放资源。
  • Dispose方法。指的是在类中实现IDisposable接口,该接口中的Dispose方法定义了显示释放由对象引用的所有非托管资源。这种方法提供了更加精确的控制方式。