背景
以前公司的代码中用到了Ninject,由于对ninject不太了解,所以看了源码中的单元测试代码,看到了有关gc的操作。
所以整理一份文章,以When(何时回收)--What(回收什么)--How(如何回收)的方式简洁的介绍下.net GC内存管理垃圾回收。
https://github.com/ninject/Ninject/blob/master/src/Ninject.Test/Integration/TransientScopeTests.cs
When 何时回收
1)当Gen 0 heap内存达到最大阀值,则触发0代GC,0代GC后Gen 0中幸存的对象进入Gen1;
当Gen 1 heap的内存达到最大阀值,则触发1代GC,1代GC将Gen 0 heap和Gen 1 heap一起进行回收,幸存的对象进入Gen2;
当Gen 2 heap内存达到最大阀值,2代GC时将Gen 0 heap、Gen 1 heap和Gen 2 heap一起回收;
Ps:Gen 0和Gen 1比较小,总和保持在16M左右;Gen2的大小由应用程序确定,可能达到几G,因此0代和1代GC的成本非常低,2代GC称为full GC,通常成本很高。 2代、1代和0代GC的频率应当大致为1:10:100。
2)手动调用GC.Collect(); //实现代码中不推荐用
3)卸载AppDomian
扩展知识 Generation 分代
托管堆内存分代算法具备一定统计学基础,对GC的性能改善效果比较明显。将对象按照生命周期分成新的、老的,根据统计分布规律所反映的结果;
对新、老区域采用不同的回收策略和算法,加强对新区域的回收处理力度,争取在较短时间间隔、较小的内存区域内,以较低成本将大量新创建但不再使用的局部对象及时回收掉。分代算法的假设前提条件:
1、大量新创建的对象生命周期都比较短,而较老的对象生命周期会更长;
2、对部分内存进行回收比基于全部内存的回收操作要快;
3、新创建的对象之间关联程度通常较强。heap分配的对象是连续的,关联度较强有利于提高CPU cache的命中率,.NET将heap分成3个代龄区域: Gen 0、Gen 1、Gen 2如右图;
Ps:0,1,2代为小对象内存区,如果大小超过85000字节,会被分到大对象内存区,在回收2代是回去回收大对象内存区, 并不会在回收时清理碎片,影响性能。
What 回收哪些对象
托管资源:
1)引用类型对象没有重写过Finalize方法(没有析构函数)
当托管堆中的对象没有被栈中的任何地址指向,那么对象没有被标记为Reachable,从而在GC Roots中已经搜索不到的;
2)引用类型对象没有重写过Finalize方法(有析构函数)
当托管堆中的对象没有被栈中的任何地址指向,那么对象没有被标记为Reachable,在Finalization Queue中找不到任何地址指向,然后在Freachable Queue中没有复活,找不到任何地址指向,在GC Roots中已经搜索不到的;
3)弱引用:当被弱引用对象引用的对象已经被回收时,GC会在这次回收中回收它。(高速缓存)
非托管资源:(非GC直接回收)
1)当引用类型对象重写过Finalize方法时(有析构函数),且用户没有手动去调用非托管对象的Dispose去手动释放非托管资源的情况下,用户可以在析构函数中通过调用非托管对象的Dispose方法,作为非托管对象的释放的保底做法。当引用对象被GC回收时,GC会去调引用对象的Finalize方法(析构函数),从而非托管对象的Dispose会被调用去释放非托管资源。
Ps:资源均是调用Object.Finalize()释放的,但无法手动直接调用,可以等GC调用,或者通过Dispose间接调用。
扩展知识
托管堆
引用类型 (实例本身)
栈
值类型(引用对象的地址也存在栈中)
非托管堆
ApplicationContext, Brush, Component, ComponentDesigner, Container, Context, Cursor, FileStream, Font, Icon, Image, Matrix, Object, OdbcDataReader, OleDBDataReader, Pen, Regex, Socket, StreamWriter, Timer, Tooltip, 文件句柄, GDI资源, 数据库连接等等资源.
GC Roots
可以理解为CLR在heap之外可以找到的各种入口点,并存在多个gc roots.
https://stackoverflow.com/questions/8458886/what-is-a-rooted-reference
https://blogs.msdn.microsoft.com/abhinaba/2009/01/30/back-to-basics-mark-and-sweep-garbage-collection/
How 如何回收
扩展知识
比较常见的gc回收算法
1) Mark Sweep (.net java) : https://blogs.msdn.microsoft.com/abhinaba/2009/01/30/back-to-basics-mark-and-sweep-garbage-collection/
2) Reference Counting : https://secweb.cs.odu.edu/~zeil/cs361/web/website/Lectures/garbageCollection/pages/referenceCounting.html
3) Copy Collection : Garbage collection is performed by copying live objects from one semispace (the from-space) to the other (the to-space), which then becomes the new heap. The entire old heap is then discarded in one piece
非托管资源的推荐清理方式(引用对象下有非托管资源)
1) 手动调用引用对象的Dispose();
2) 在析构函数中调用非托管资源实现的Dispose()做为保底;
参考资料
《CLR via c#》