在.NET程序开发中,为了将开发人员从繁琐的内存管理中解脱出来,将更多的精力花费在业务逻辑上,CLR提供了自动执行垃圾回收的机制来进行内存管理。开发人员甚至感受不到这一过程的存在。CLR执行垃圾回收的过程,有如下几点:算法
垃圾回收,第一步要判断哪些对象须要被GC回收掉。简而言之,那些在代码的任何位置也没法访问到的对象是须要被GC回收掉的。函数
GC借助于应用程序根(Application Roots)和对象图(Object Graph)来判断哪些对象须要被回收。应用程序根保存了堆上对象的引用,若是一个对象没有直接或者间接的被应用程序根所引用,那么就说明没有任何代码能够访问到它,所以这个对象能够被回收。性能
应用程序根只是一个入口点,一个对象可能持有其余一个或者多个对象的引用,这种对象间的引用关系构成了对象图。每次建立新对象,在复制引用、删除引用,或者执行垃圾回收以后,CLR都会自动更新它。优化
.NET中的对象建立在托管堆上,托管堆维护着一个指针,这个指针标识了下一个将要建立的新对象所在位置,一般称做新对象指针。该指针老是位于托管堆的末尾。当使用new运算符建立对象时,CLR将会执行下面几个主要操做:指针
GC有许多算法,常见的算法有Reference Counting, Mark Sweep, Copy Collection等,目前主流的虚拟系统.NET CLR, Java VM都采用的Mark Sweep算法。Mark-Sweep标记清除阶段:先假设Managed Heap中全部对象均可以被回收,而后找出不能回收的对象,给这些对象打上标记,最后Managed Heap中没有打标记的对象均可以被回收。Compact压缩阶段:对象回收以后Managed Heap内存空间变得不连续,在Managed Heap中移动这些对象,使他们从新从Managed Heap基地址开始连续排列。对象
执行垃圾回收的时机:内存
垃圾回收过程当中还采用了一些优化策略,主要时对象代(Object Generation)和大对象堆(Large Object Heap)开发
对象共分了三个代级:内存管理
当进行垃圾回收时,垃圾回收器将会首先检查全部的第0代对象,并对其中可回收的对象进行清理。若是清理后得到足够的空间,经历过垃圾回收后的对象将提高为第1代对象。io
若是全部的第0代对象都检查过了,可是内存空间还不够,那么将会检查第1代对象的可访问性,并进行垃圾回收。此时,若是经历过垃圾回收的第1代对象仍保留在堆上,则会升级为第2代对象。相似的,若是内存仍不够用,将会对第2代对象进行检查和垃圾回收。若是第2代的部分对象在这次垃圾回收后仍然保留在堆栈上,它依然是第2代对象。由于总共定义了3代对象。若是第2代对象在进行完垃圾回收后空间依然不够用,则会抛出OutOfMemoryException异常。
可见,最容易清理掉的就是那些新对象。
垃圾回收的过程当中有一个很影响性能的地方,就是在压缩的过程当中,由于要批量地挪动对象,以填充腾出来的空间,若是对象很大,那么要移动的数据量就会很大。
若是将大对象直接分配在第0代,那么第0代的空间很快就会被占满,从而迫使CLR执行一次垃圾回收,这样执行垃圾回收的次数就会变得很频繁。
所以,第二个优化策略就是采用大对象堆,当对象大小超过85kb时,就会被分配在大对象堆上。大对象堆有几个特色:
至此,就对.NET GC有了一个基本的了解。感谢您的阅读~