zoukankan      html  css  js  c++  java
  • C#内存管理和垃圾回收机制

    • 数据类型
    • 垃圾回收机制

    一、数据类型

    C#中的数据类型分为值类型 (Value type) 引用类型(reference type),

    值  类 型: 所有的值类型都集成自 System.ValueType 上,除非加声明?否则不可为null,保存在 堆栈(Stack,先进后出)上,常见的值类型有:整形、浮点型、bool、枚举等。

    引用类型:所有的引用类型都继承自System.Object 上,引用类型保存在 托管堆(Head,先进先出)上,常见的类型有:数组、字符串、接口、委托、object等。

    拆箱和装箱:引用类型和值类型的相互转换叫做拆装箱操作。

    拆箱:拆箱就是将一个引用型对象转换成任意值型!比如:

    int i=0;
    System.Object obj=i;
    int j=(int)obj;

     装箱:装箱就是隐式的将一个值型转换为引用型对象。比如:

    int i=0;
    Syste.Object obj=i;

    二、垃圾回收机制 GC

      1、简介

          C#中和Java一样是一种系统自动回收释放资源的语言,在C#环境中通过 GC(Garbage Collect)进行系统资源回收,在数据基本类型中介绍到,C#数据类型分为引用类型和值类型,

    值类型保存在Stack上,随着函数的执行作用域执行完毕而自动出栈,所以这一类型的资源不是GC所关心 对象。GC垃圾回收主要是是指保存在Heap上的资源。

           .NET的GC机制有这样两个问题:
      首先,GC并不是能释放所有的资源。它不能自动释放非托管资源。
      第二,GC并不是实时性的,这将会造成系统性能上的瓶颈和不确定性。
      GC并不是实时性的,这会造成系统性能上的瓶颈和不确定性。所以有了IDisposable接口,IDisposable接口定义了Dispose方法,这个方法用来供程序员显式调用以释放非托管资源。使用using语句可以简化资源管理。
      

      2、托管资源和非托管资源

    上面介绍到,GC只释放托管资源,那么什么是托管资源和费托管资源。

      托管资源   : 托管资源指的是.NET可以自动进行回收的资源,主要是指托管堆上分配的内存资源。托管资源的回收工作是不需要人工干预的,有.NET运行库在合适调用垃圾回收器进行回收。

      非托管资源:非托管资源指的是.NET不知道如何回收的资源,最常见的一类非托管资源是包装操作系统资源的对象,例如文件,窗口,网络连接,数据库连接,画刷,图标等。这类资源,

    垃圾回收器在清理的时候会调用Object.Finalize()方法。默认情况下,方法是空的,对于非托管对象,需要在此方法中编写回收非托管资源的代码,以便垃圾回收器正确回收资源。

    总结:托管资源是释放由GC来完成,释放的时间吧不一定,一般是系统感觉内存吃紧,会进行紧急回收资源。一个对象想成为被回收,首先需要成为垃圾,GC是通过判断对象及其子对象有没有指向有效的引用,

    如果没有GC就认为它是垃圾。垃圾回收机制通过一定的算法得到哪些没有被被引用、或者不再调用的资源,当这些垃圾达到一定的数量时,回启动垃圾回收机制,GC回收实际上是调用了析构函数。

    垃圾回收机制意味着你不需要担心处理不再需要的对象了。咱们关心的主要是非托管资源的释放。

    垃圾回收时对象一共有三代 :0,1,2。每一代都有自己的内存预算,空间不足的时候会调用垃圾回收。为了提高性能都是按代回收,第0代超预算之后就回收第0代的对象,而存活下来的对象就提升为第1代,

    依次类推,而往往经过多次0代的垃圾回收才能回收一次第1代。

    GC进行垃圾回收是系统决定的,下面是进行强制回收的执行代码(非特殊情况下不要使用此方法,会影响系统效率,削弱垃圾回收器中优化引擎的作用,而垃圾回收器可以确定运行垃圾回收的最佳时间)

    //对所有代进行垃圾回收。
    GC.Collect();
    //对指定的代进行垃圾回收。
    GC.Collect(int generation); 
    //强制在 System.GCCollectionMode 值所指定的时间对零代到指定代进行垃圾回收。
    GC.Collect(int generation, GCCollectionMode mode); 

      3、非托管资源的释放

    在定义一个类时,可以使用两种不同的机制类释放非托管资源,这两周机制有时候通常放在一起使用

    1、声明析构函数(终结器)吗,作为类的成员

      构造函数可以在创建对象实例的时候执行某些操作,析构函数正好相反是资源创建以后被系统回收的时候执行的操作,垃圾回收器在回收对象之前会调用析构函数,所以在函数代码块中可以写释放非

    托管资源的代码。析构函数没有返回值,没有参数,没有修饰符

    复制代码
        public class AA
            {
                ~AA()
                {
                    //析构函数语法
                }
            }
    复制代码

    析构函数会被编辑器翻译成下面的代码:

    复制代码
           protected override void Finalize()
            {
                try
                {
                    // Cleanup statements...     
                }
                finally
                {
                    base.Finalize();
                }
            }
    复制代码

    最终析构函数会被翻译成上面的代码块,重写基类的Finalize()方法,然后最终调用 Base.Finalize()方法。

    注意!大量的使用析构函数会影响效率!带有析构函数的对象会被系统执行两次才会被释放掉。GC执行释放资源时,没有析构函数的资源会被直接释放掉,假如目标对象有析构函数,会被先放进一个叫做“终结队列”的

    项中去,然后系统调用另一个高优先级线程来执行 Finalize()方法,GC继续回收其它对象。等方法执行完以后会将对象从终结队列中清除出去,此时对象才是真正意义上的垃圾。等GC执行资源回收的时候,才回释放掉终结队列里面的对象。

    总结:

    • 托管堆中内存的释放和析构函数的执行分别属于两个不同的线程。

    • 带有析构函数的对象其生命周期会变长,由上知会进行两次垃圾回收处理才能被释放,如此一来将导致程序性能的下降。

    • 若一个对象引用了其他对象时,当此对象不能够被释放时,则其引用对象也就无法进行内存的释放,也就意味着带有析构函数的对象和其引用对象将从第0代提升到第一代,毫无疑问将影响程序的性能。

    综上所述,建议是不要实现其析构函数,这将大大降低程序的性能。

    2、在类中实现 System.IDisposable 接口

     实现IDisposable接口来显示释放系统资源

    class Test:IDisposable  
        {
    
            #region IDisposable Support
            private bool disposedValue = false; // 要检测冗余调用
    
            protected virtual void Dispose(bool disposing)
            {
                if (!disposedValue)
                {
                    if (disposing)
                    {
                        // TODO: 释放托管状态(托管对象)。
                    }
    
                    // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                    // TODO: 将大型字段设置为 null。
    
                    disposedValue = true;
                }
            }
    
            // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
            ~Test()
            {
                // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
                Dispose(false);
            }
    
            // 添加此代码以正确实现可处置模式。
            public void Dispose()
            {
                // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
                Dispose(true);
                // TODO: 如果在以上内容中替代了终结器,则取消注释以下行。
                // GC.SuppressFinalize(this); 
            }
            #endregion 
        }

    ① 当我们显示调用Dispose()方法以后,会执行释放费托管资源的操作,然后disposedValue会为Flase,所以我们多次调用也没有关系。Dispose()调用执行完以后,执行  GC.SuppressFinalize(this)(告诉GC不再执行终结器操作)  代码

    ② 如果我们不调用 Diapose()方法,系统会调用使用终结器操作,最后也是释放非托管资源。

    从例子可以看出,对于手动回收(disposing为true),除了非托管资源,还可以通知其他托管对象Dispose(),因为这时候内部的托管对象肯定没回收。而到了自动回收,就不能通知其他托管对象了,因为垃圾回收可能已经把他们回收了,
    而且垃圾回收会自动回收他们,也不用你通知了。

    总结:

    当释放非托管资源时我们应该显式的去实现Dipose()方法或者Close()方法,但是万一我们忘记显式去调用方法,此时还有一条退路,CLR会自动调用Finalize()方法,

    很显然调用Finalize()方法会大大降低程序的性能,没关系,上述释放模式关键的一点是通过手动释放调用Dispose()方法可以阻止Finalize()方法的调用,换言之,上述通过手动释放既释放了非托管资源又加快了程序运行的速度,毫无疑问,这是一种完美的解决方案。

    1、Finalize是系统决定执行的,我们无法干涉。Dispose是可以我们调用来释放的。

    2、Finalize只能释放费托管资源,Dispose既可以释放托管资源也可以释放非托管资源。

  • 相关阅读:
    博客备份小工具3
    博客转发小工具1
    04-属性选择器
    05-伪类选择器
    03-高级选择器
    02-css的选择器
    01-css的引入方式
    函数进阶-(命名空间、作用域、函数嵌套、作用域链、函数名本质、闭包)
    函数基础-(引子、定义函数、调用函数、函数返回值、函数参数)
    Python之路【第08章】:Python函数
  • 原文地址:https://www.cnblogs.com/bruce1992/p/13992586.html
Copyright © 2011-2022 走看看