(转).net平台下垃圾回收机制

引言:使用c++进行编程,内存的处理绝对是让每个程序设计者最头疼的一块了。但是对于.net平台下使用c#语言开发系统,内存管理可以说已经不算是问题了。在.net平台下CLR负责管理内存,CLR中的垃圾收集器GC:Garbage Collection,负责执行内存的清理工作,但是GC也只是负责清理托管堆上的垃圾对象,而对于非托管的资源对象,GC则不起作用,必须要程序开发者手动清理。此处需要稍微说明:一般而言,非托管资源主要包括数据库链接、文件句柄、COM对象、套接字、GDI+对象、互斥体等等。

在介绍GC前,有必要对.net中CLR管理内存区域做简要介绍:

  1、 堆栈:用于分配值类型实例。堆栈主要操作系统管理,而不受垃圾收集器的控制,当值类型实例所在方法结束时,其存储单位自动释放。栈的执行效率高,但存储容量有限。

  2 、GC堆:用于分配小对象实例。如果引用类型对象实例的大小小于85000字节,实例将被配置在GC堆上,当有内存分配或者回收时,垃圾收集器可能会对GC堆进行压缩。

  3、 LOH:large object heap,用于分配大对象实例。如果引用类型对象的实例的大小不小于85000字节时,该实例将被分配到LOH堆上,而LOH堆不会被压缩,而且只在完全GC回收时被回收。

  

  既然要清理垃圾,那么必然要明白什么是垃圾吧,垃圾的理解:一个对象成为“垃圾”表示该对象不被任何其他对象所引用。因此GC必须采用一定的算法在托管堆中遍历所有对象,最终形成一个可达对象图,而不可达的对象将成为被释放的垃圾对象等待收集。

  在明白了什么是垃圾后,肯定会对GC如何回收垃圾提出疑问。.net平台下,每个应用程序都有一组根(指针),它指向托管堆中的存储位置,由JIT编译器和CLR运行时维护根指针列表,主要包括全局变量、静态变量、局部变量和寄存器指针等。GC正是通过根指针列表来获得托管堆中的对象图,其中定义了应用程序根引用的托管堆中的对象,当GC启动时,它假设所有对象都是可回收的垃圾,开始遍历所有的根,将根引用的对象标记为可达对象添加到可达对象图中,在遍历过程中,如果根引用的对象还引用着其他对象,则该对象也被添加到可达对象图中,依次类推,GC通过根列表的递归遍历,将能找到所有可达对象,并形成一个可达对象图。同时那些不可达对象则被认为是可回收对象,GC接着运行垃圾收集进程来释放垃圾对象的内存空间。这种收集算法称为:标记和清除收集算法。

垃圾回收一般在下列情况下进行:

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方法。

时间: 2024-07-31 10:13:43

(转).net平台下垃圾回收机制的相关文章

.net平台下垃圾回收机制

引言:使用c++进行编程,内存的处理绝对是让每个程序设计者最头疼的一块了.但是对于.net平台下使用c#语言开发系统,内存管理可以说已经不算是问题了.在.net平台下CLR负责管理内存,CLR中的垃圾收集器GC:Garbage Collection,负责执行内存的清理工作,但是GC也只是负责清理托管堆上的垃圾对象,而对于非托管的资源对象,GC则不起作用,必须要程序开发者手动清理.此处需要稍微说明:一般而言,非托管资源主要包括数据库链接.文件句柄.COM对象.套接字.GDI+对象.互斥体等等. 在

V8 下的垃圾回收机制

V8 实现了准确式 GC,GC 算法采用了分代式垃圾回收机制.因此,V8 将内存(堆)分为新生代和老生代两部分. 1.新生代算法 新生代中的对象一般存活时间较短,使用 Scavenge GC 算法. 在新生代空间中,内存空间分为两部分,分别为 From 空间和 To 空间.在这两个空间中,必定有一个空间是使用的,另一个空间是空闲的.新分配的对象会被放入 From 空间中,当 From 空间被占满时,新生代 GC 就会启动了.算法会检查 From 空间中存活的对象并复制到 To 空间中,如果有失活

Java深度历险(四)——Java垃圾回收机制与引用类型

Java语言的一个重要特性是引入了自动的内存管理机制,使得开发人员不用自己来管理应用中的内存.C/C++开发人员需要通过malloc/free 和new/delete等函数来显式的分配和释放内存.这对开发人员提出了比较高的要求,容易造成内存访问错误和内存泄露等问题.一个常见的问题是会产生“悬挂引用(dangling references)”,即一个对象引用所指向的内存区块已经被错误的回收并重新分配给新的对象了,程序如果继续使用这个引用的话会造成不可预期的结果.开发人员有可能忘记显式的调用释放内存

JVM内存管理机制和垃圾回收机制

JVM自身结构物理图: Java代码编译和执行的整个过程包含了以下三个重要的机制: 1.java源码编译机制 1)分析和输入到符号表 class文件结构包含: 结构信息.包括class文件格式版本号及各部分的数量与大小的信息 元数据.对应于Java源码中声明与常量的信息.包含类/继承的超类/实现的接口的声明信息.域与方法声明信息和常量池 方法信息.对应Java源码中语句和表达式对应的信息.包含字节码.异常处理器表.求值栈与局部变量区大小.求值栈的类型记录.调试符号信息 2.类加载机制 1)Boo

浅析JVM垃圾回收机制

首先我们需要知道Java的内存分配与回收全部由JVM垃圾回收机制自动完成.每种JVM实现可能采用不同的方法实现垃圾回收机制.在收购SUN之前,Oracle使用的是JRockit JVM,收购之后使用HotSpot JVM.目前Oracle拥有两种JVM实现并且一段时间后两个JVM实现会合二为一.HotSpot JVM是目前Oracle SE平台标准核心组件的一部分.市面上探讨垃圾回收机制,默认都是基于HotSpot JVM的.Ok,我们切入正题,先来看下JVM的内存区域模型, 这张图非常直观的展

JVM的垃圾回收机制详解和调优

JVM的垃圾回收机制详解和调优 gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存.java语言并不要求jvm有gc,也没有规定gc如何工作.不过常用的jvm都有gc,而且大多数gc都使用类似的算法管理内存和执行收集操作. 1.JVM的gc概述 gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存.java语言并不要求jvm有gc,也没有规定gc如何工作.不过常用的jvm都有gc,而且大多数gc都使用类似的算法管理内存和执行收集操作. 在充分理解了垃圾收集算法和执行

java有自动垃圾回收机制

当垃圾收集器判断已经没有任何引用指向对象的时候,会调用对象的finalize方法来释放对象占据的内存空间~ java中垃圾回收以前听老师讲好像是内存满了他才去做一次整体垃圾回收,在回收垃圾的同时会调用finalize方法.你在构造一个类时可以构造一个类时覆盖他的finalize方法以便于该类在被垃圾回收时执行一些代码,比如释放资源. 1.JVM的gc概述 gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存.java语言并不要求jvm有gc,也没有规定gc如何工作.不过常用的jvm

java 垃圾回收机制 引用类型

Java语言的一个重要特性是引入了自动的内存管理机制,使得开发人员不用自己来管理应用中的内存.C/C++开发人员需要通过malloc/free 和new/delete等函数来显式的分配和释放内存.这对开发人员提出了比较高的要求,容易造成内存访问错误和内存泄露等问题.一个常见的问题是会产生“悬挂引用(dangling references)”,即一个对象引用所指向的内存区块已经被错误的回收并重新分配给新的对象了,程序如果继续使用这个引用的话会造成不可预期的结果.开发人员有可能忘记显式的调用释放内存

Java垃圾回收机制以及内存泄露

1.Java的内存泄露介绍 首先明确一下内存泄露的概念:内存泄露是指程序运行过程动态分配了内存,但是在程序结束的时候这块内存没有被释放,从而导致这块内存不可用,这就是内存 泄露,重启计算机可以解决这个问题,但是有可能再次发生内存泄露,内存泄露与硬件没有关系,它是软件设计的缺陷所导致的. Java发生内存泄露的原因很明确,就是长声明周期对象持有短声明周期对象的引用就很可能发生内存泄露.尽管短生命周期对象已经不再需要,但是因为长生命 周期对象在持有它的引用而导致它不能被GC回收,这就是Java内存泄