zoukankan      html  css  js  c++  java
  • C#的垃圾回收机制及弱引用

          在上一篇中,讨论了字符串常量的拘留池和不可变性;对于字符串变量,没有这个特性(或其他DotNet的非托管资源),当我们使用完后就要手动回收,即将变量的值指向null(p=null),然而堆内存中,那个没有任何变量引用的对象并没有立即回收(还占用一定量的堆内存),所以当我们要进行一个相当耗时且最好不要中断的操作时,最好调用垃圾回收,回收内存中的“垃圾”(没有变量引用的对象和非托管资源)以保证内存足够使用,这里提一下,所谓非托管资源,指的是非Dotnet开辟的资源,用完后再调用释放资源,如:数据库连接中的SqlConnection,SqlCommand等对象,用完要调用Dispose()释放,通常使用这类对象用using(),保证离开using范围,对象被回收。

       接下来,说一下垃圾回收机制(垃圾回收机制只回收托管堆中的内存资源),在DotNet中有“代”的概念(共3代),举例说明:

    同时,我们假设第0代大小为256M,第1代是512M,第2代是1G。程序执行一段时间后,定义了3个变量d、e、f,第1代中b指向null,但是第0代放不下,那么Dotnet会提升第0代中存活变量的代,结果如下:

    程序继续执行一段时间,变量C又指向null,同时定义3个变量g,h,i,DotNet看到第0代放不下,继续提升第0代变量的代同时将新变量放入第1代

    依次类推,直至3代的内存消耗殆尽,此时DotNet会提升3个代的存储空间,保证变量能放进去。直至提升代的空间后,仍放不下,此时就内存溢出,抛异常了。当我们开始就定义一个大对象---1G的对象),将被直接放入第2代。

    了解了代的概念,开始上代码:

    1  Person p = new Person() { ID = 1, Name = "张三", BirthDate = DateTime.Now };  //对象初始化器
    2             p = null;
    3             GC.Collect();
    4             Console.WriteLine(p.ID+"=="+p.Name+"=="+p.BirthDate);
    View Code

     GC.Collect()就是告诉DotNet,请垃圾回收一把。这时,DotNet会将程序暂停,保存变量的当前状态,进行垃圾回收,回收完毕,重新分配内存,让程序继续执行(因为程序会暂停执行,所以对性能会有影响,所以一般不建议经常调用GC.Collect()来执行垃圾回收),上面的代码会抛空引用异常。

         弱引用,指的是一个变量可以被垃圾回收了。这个对象不会用了,但是日后又担心会用,而且这个对象的创建非常耗时,我们就要用弱引用引用起来,需要注意的是,弱引用引用起来的对象是可以被垃圾回收的,但只要没被垃圾回收(能引用得到),就可以继续使用。

     1 Person p = new Person() { ID = 1, Name = "张三", BirthDate = DateTime.Now };  //对象初始化器
     2             
     3             WeakReference wReference = new WeakReference(p);        //将p对象弱引用起来
     4             p = null;
     5 
     6             GC.Collect();           //手动调用垃圾回收
     7             object o = wReference.Target;      
     8             if (o!=null&&wReference.IsAlive)
     9             {
    10                 Person p1 = o as Person;
    11                 Console.WriteLine(p1.ID + "==" + p1.Name + "==" + p1.BirthDate);
    12             }
    13             else
    14             {
    15                 Console.WriteLine("对象被垃圾回收了,请重新创建对象吧");
    16             }
    View Code

    运行可知。这是因为我们手动调用了GC.Collect(),把这句话注释掉,再运行,

    Person p = new Person() { ID = 1, Name = "张三", BirthDate = DateTime.Now };  //对象初始化器
                
                WeakReference wReference = new WeakReference(p);        //将p对象弱引用起来
                p = null;
    
                //GC.Collect();           //手动调用垃圾回收
                object o = wReference.Target;      
                if (o!=null&&wReference.IsAlive)
                {
                    Person p1 = o as Person;
                    Console.WriteLine(p1.ID + "==" + p1.Name + "==" + p1.BirthDate);
                }
                else
                {
                    Console.WriteLine("对象被垃圾回收了,请重新创建对象吧");
                }
    View Code

    运行结果如下:

    由此可见,虽然将p指向了null,但是因为还没被回收,我们通过弱引用(WeakReference)能找到它,便可继续使用。

    在使用弱引用时,要注意:

          一定是先object o = wReference.Target; 将对象强引用起来,再加判断,如果先判断wReference.Target != null,再执行object o = wReference.Target时正巧p被垃圾回收了,那么o就是null。

    弱引用的适用场景:

    1.创建一个对象非常耗时;

    2.这个对象不会用了(可以被垃圾回收),但是日后又担心会用;

    3.弱引用的对象也不是一定就存在,如果手动调用GC.Collect()或者正好被垃圾回收,也会引用不到,此时若要用,只能重新创建对象。

  • 相关阅读:
    Java高级之类结构的认识
    14.8.9 Clustered and Secondary Indexes
    14.8.4 Moving or Copying InnoDB Tables to Another Machine 移动或者拷贝 InnoDB 表到另外机器
    14.8.3 Physical Row Structure of InnoDB Tables InnoDB 表的物理行结构
    14.8.2 Role of the .frm File for InnoDB Tables InnoDB 表得到 .frm文件的作用
    14.8.1 Creating InnoDB Tables 创建InnoDB 表
    14.7.4 InnoDB File-Per-Table Tablespaces
    14.7.2 Changing the Number or Size of InnoDB Redo Log Files 改变InnoDB Redo Log Files的数量和大小
    14.7.1 Resizing the InnoDB System Tablespace InnoDB 系统表空间大小
    14.6.11 Configuring Optimizer Statistics for InnoDB 配置优化统计信息用于InnoDB
  • 原文地址:https://www.cnblogs.com/chens2865/p/3838787.html
Copyright © 2011-2022 走看看