引言:使用c++进行编程,内存的处理绝对是让每一个程序设计者最头疼的一块了。可是对于.net平台下使用c#语言开发系统,内存管理能够说已经不算是问题了。在.net平台下CLR负责管理内存,CLR中的垃圾收集器GC:Garbage Collection,负责执行内存的清理工做,可是GC也只是负责清理托管堆上的垃圾对象,而对于非托管的资源对象,GC则不起做用,必需要程序开发者手动清理。此处须要稍微说明:通常而言,非托管资源主要包括数据库连接、文件句柄、COM对象、套接字、GDI+对象、互斥体等等。c++
在介绍GC前,有必要对.net中CLR管理内存区域作简要介绍:算法
一、 堆栈:用于分配值类型实例。堆栈主要操做系统管理,而不受垃圾收集器的控制,当值类型实例所在方法结束时,其存储单位自动释放。栈的执行效率高,但存储容量有限。数据库
2 、GC堆:用于分配小对象实例。若是引用类型对象实例的大小小于85000字节,实例将被配置在GC堆上,当有内存分配或者回收时,垃圾收集器可能会对GC堆进行压缩。编程
三、 LOH:large object heap,用于分配大对象实例。若是引用类型对象的实例的大小不小于85000字节时,该实例将被分配到LOH堆上,而LOH堆不会被压缩,并且只在彻底GC回收时被回收。c#
既然要清理垃圾,那么必然要明白什么是垃圾吧,垃圾的理解:一个对象成为“垃圾”表示该对象不被任何其余对象所引用。所以GC必须采用必定的算法在托管堆中遍历全部对象,最终造成一个可达对象图,而不可达的对象将成为被释放的垃圾对象等待收集。性能优化
在明白了什么是垃圾后,确定会对GC如何回收垃圾提出疑问。.net平台下,每一个应用程序都有一组根(指针),它指向托管堆中的存储位置,由JIT编译器和CLR运行时维护根指针列表,主要包括全局变量、静态变量、局部变量和寄存器指针等。GC正是经过根指针列表来得到托管堆中的对象图,其中定义了应用程序根引用的托管堆中的对象,当GC启动时,它假设全部对象都是可回收的垃圾,开始遍历全部的根,将根引用的对象标记为可达对象添加到可达对象图中,在遍历过程当中,若是根引用的对象还引用着其余对象,则该对象也被添加到可达对象图中,依次类推,GC经过根列表的递归遍历,将能找到全部可达对象,并造成一个可达对象图。同时那些不可达对象则被认为是可回收对象,GC接着运行垃圾收集进程来释放垃圾对象的内存空间。这种收集算法称为:标记和清除收集算法。ide
垃圾回收通常在下列状况下进行:函数
1 内存不足溢出时,更确切的应该说是第0代对象充满时。性能
2 调用GC.Collect方法强制执行垃圾回收。(通常不要执行此方法)优化
3 Windows报告内存不足时,CLR将强制执行垃圾回收。
4 CLR卸载AppDomain时,GC将对全部代龄的对象执行垃圾回收。
5 其余状况,如物理内存不足,超出短时间存活代的内存段门限,运行主机拒绝分配内存等。
垃圾回收运行机制:
垃圾收集器将托管堆中对象分为三代:0、1和2,在CLR初始化时,会选择为三代设置不一样的阙值容量,通常为:第0代大约为256KB,第1代2MB,第2代10MB。容量越大效率越低,而GC收集器会自动调节其阙值容量来提高执行效率。在CLR初始化后,首先添加到托管堆中的对象都被定位第0代对象,当有垃圾回收执行时,未被回收的对象代龄将提高一级,变成第1代对象,然后新建对象仍未第0代对象。代龄越小表示对象越新,一般状况下其生命周期也最短,所以GC老是先收集第0代的不可达对象内存。
随着对象的不断建立,垃圾收集再次启动时则只会检查0代对象并回收0代垃圾对象。而1代对象因为未达到1代容量阙值,则不会进行垃圾回收操做,从而有效地提升了垃圾收集的效率,而这也是代龄机制在垃圾回收中的性能优化做用。当第0代对象释放的内存不足以建立新的对象,同时1代对象的体积也超出了容量阙值是,垃圾收集器将同时对0代和1代对象进行垃圾回收。回收以后,未被回收的1代对象变化2级对象,未被回收的0代对象升级为1代对象,然后新建的对象仍为第0代对象。
注:微软强烈建议不要经过GC.Collect方法来强制执行垃圾收集,这样会妨碍GC自己的工做方式,经过Collect会使对象代龄不断提高,扰乱应用程序的内存使用。只有在明确知道有大量对象中止引用时,才考虑使用GC.Collect方法来调用收集器。
上面介绍了垃圾管理器GC清理托管资源所涉及的一些机理,然而对于非托管资源,须要开发者手动清理,方法主要有:Finalize方法和Dispose方法。
Finalize:
Finalize方法又称为终止化操做:经过对自定义类型实现一个Finalize方法来释放非托管资源,而终止化操做在对象的内存回收以前经过调用Finalize方法来释放资源。在析构函数中重写Finalize方法,当垃圾管理器启动时,对于断定为可回收的垃圾对象,GC会自动执行其Finalize方法清理非托管资源。
protected override void Finalize() { try { //执行自定义资源清理操做 } finally { base.Finalize(); } }
Finalize的缺点是:
终止化操做的时间没法控制,执行顺序也不能保证。
Finalize方法会极大的损失性能,GC使用一个终止话队列的内部结构来跟踪具备Finalize方法的对象。
重写finalize方法的类型对象,其引用类型对象的代龄将被提高,带来内存压力。
Dispose:
Dispose模式的实现是:定义的类型必须实现System.IDisposable接口,该接口中定义了一个公有无参数的Dispose方法,程序设计者能够在Dispose方法中实现对非托管资源的清理工做。
下面编写一个项目中遇到使用Dispose方法的例子,功能是在套接字使用完毕后释放资源
public class SocketConnection : IDisposable { //逻辑操做 //..................... //实现Dispose public void Dispose() { try { this.ClientSock.Shutdown(SocketShutdown.Both); this.ClientSock.Close(); this.Server = null; } catch (Exception ex) { } } }
总结:
在.net中,在堆栈上分配的资源在调用结束后,其内存自动会释放。
托管堆中的资源,由CLR的垃圾管理器进行清理操做。
对于非托管资源,必须由程序设计者进行操做,而对于Finalize和Dispose,最好采用Dispose方法。