前记:
之所以研究GC是因为之前写的一个实时截屏软件(每隔一段时间截一次屏),发现它的内存消耗惊人,往往开始运行时3M左右,但是几十分钟后就飙到600+M。于是开始研究GC,优化后现在一直保持在4~5M。
一、什么是GC
关于GC,是个比较大的话题,很多平台都有GC机制,除了C#还有java,甚至Javascript等。我们的话题主要面对C#。
C#的GC是一种.Net特性,它不断回收不再使用的托管资源对象,为将要使用内存的对象开辟空间。回收的过程是由.Net自动运行管理的,但是程序员也可以通过代码干预。
二、托管资源与非托管资源
托管资源指托管于.Net机制的资源对像,如int,double,float等等。.Net中80%都是托管资源,他们会被GC及时清理。
非托管资源指非.Net机制托管对象,如图像对象,数据库连接对象等等。
非托管资源大体有:
ApplicationContext,Brush,Component,ComponentDesigner,Container,Context,Cursor,
FileStream,Font,Icon, Image,Matrix, Object,OdbcDataReader,OleDBDataReader,Pen,
Regex,Socket, StreamWriter,Timer,Tooltip 等
FileStream,Font,Icon, Image,Matrix, Object,OdbcDataReader,OleDBDataReader,Pen,
Regex,Socket, StreamWriter,Timer,Tooltip 等
三、清理非托管资源
GC会自动回收托管资源对象,但是非托管资源对象GC并不知道如何处理,这就需要程序员处理非托管资源的清空方式。
实现方式有两种:
实现IDisposable接口的DIspose方法;
实现析构方法;
两者的区别在于,当一个类实现IDisposable接口后,在使用该类时通过主动调用Dispose方法,或者使用using语句使用该类(using语句执行完毕会执行Dispose方法,即便出现中间异常),皆可及时清理非托管资源。
实现的析构方法却属于“被动”,只有GC执行清理时,若发现该类有析构方法,就会主动调用。
两者并不冲突,但是实际上一个类只需要一种就够了。而且建议使用Dispose方式。
四、GC底层原理
在GC中有一个计数器,他会在一个对象创建时,将其纳入计数器,并计数为0。当某处使用该对象时,计数+1,当某处该对象使用完毕,或置为null,则计数-1。(计数数值永远保持大于等于0.)
当GC开启回收并发现一个对象的计数为0时,会将其清理掉,释放对应内存。
托管资源由于是由.Net机制完全控制监管的,他的计数会严格且有效,所以能够及时清理。但是非托管资源由于.Net不清楚该对象会在什么时候不再使用,其计数方式就需要程序员手动管理。当手动处理计数之后(通过Dispose或析构),GC就会知道谁应该被清理,非托管资源也就顺利被GC回收了。