• C#学习笔记(十四):GC机制和弱引用


    垃圾回收(GC)

    垃圾回收即Garbage Collector,垃圾指的是内存中已经不会再使用的对象,通过收集释放掉这些对象占用的内存。

    GC以应用程序的root为基础,遍历应用程序在Heap上动态分配的所有对象,通过识别它们是否被引用来确定哪些对象是已经死亡的、哪些仍需要被使用。已经不再被应用程序的root或者别的对象所引用的对象就是已经死亡的对象,即所谓的垃圾,需要被回收。

    关于C#使用的垃圾回收算法可以点击这里查看。

    析构函数

    析构函数会在GC执行清楚当前对象时被调用,可以在析构函数中执行一些释放方法。

    为了优化GC算法,微软使用了“代”的概念,介绍如下:

    堆里面总共有3代。

    譬如,当程序运行时,有对象需要存储在堆里面,GC就会创建第1代(假设空间大小为256K),对象就会存储在第0代里面,当程序继续运行,运行到第0代的大小不足以存放对象,这时候就就会创建第1代(假设空间为10M),GC就会把第0代里面的“垃圾对象”清理掉,把“活着”的对象放在第1代,这时候第0代就空了,用于存放新来的对象,当第0代满了的时候,就会继续执行以上操作,随着程序的运行,第1代不能满足存放要求,这时候就会创建第2代,清理方式如上相同。

    我们来看一个例子:

     1 using System;
     2 
     3 namespace Study
     4 {
     5     class Program
     6     {
     7         static void Main(string[] args)
     8         {
     9             Test test = new Test();
    10             
    11             //对象会被分配到第 0 代
    12             Console.WriteLine("test对象所在的代:" + GC.GetGeneration(test));
    13             //回收对象, 这时 test 会被分配到第 1 代
    14             GC.Collect();
    15             Console.WriteLine("test对象所在的代:" + GC.GetGeneration(test));
    16             //回收对象, 这时 test 会被分配到第 2 代
    17             GC.Collect();
    18             Console.WriteLine("test对象所在的代:" + GC.GetGeneration(test));
    19             //回收对象, 最多只有3个代, 所以 test 还在第 2 代
    20             GC.Collect();
    21             Console.WriteLine("test对象所在的代:" + GC.GetGeneration(test));
    22 
    23             //断开引用, 对象会被回收
    24             test = null;
    25             //回收对象, test 会被回收并调用析构函数
    26             GC.Collect();
    27 
    28             Console.Read();
    29         }
    30     }
    31 
    32     public class Test
    33     {
    34         ~Test()
    35         {
    36             Console.WriteLine("Test被回收了!");
    37         }
    38     }
    39 }

    运行结果如下:

    1 test对象所在的代:0
    2 test对象所在的代:1
    3 test对象所在的代:2
    4 test对象所在的代:2
    5 Test被回收了!

    何时GC

    .Net何时执行GC在《C#高级编程》书中也只是简单的一句“垃圾回收会在运行库认为需要他时运行。”带过,总体而言,GC运行策略已经被微软进行优化过了,我们不需要过多的关心即可。

    托管对象和非托管对象

    在C#中,很大部分的对象都是托管对象,托管对象的释放直接由GC来处理,但也存在部分非托管对象,这些对象GC是不能对其进行自动回收的

    非托管对象

    即不受运行时管理的资源对象(如窗口句柄 (HWND)、数据库连接等)。

    非托管的对象如下:ApplicationContext,Brush,Component,ComponentDesigner,Container,Context,Cursor,FileStream,Font,Icon,Image,Matrix,Object,OdbcDataReader,OleDBDataReader,Pen,Regex,Socket,StreamWriter,Timer,Tooltip ,文件句柄,GDI资源,数据库连接等等资源。

    比如:当我们使用一个System.IO.StreamReader的一个文件对象,必须显示的调用对象的Close()方法关闭它,否则会占用系统的内存和资源,而且可能会出现意想不到的错误。

    Finalize

    当我们声明一个析构函数时实际上编译器就会自动添加下面的代码:

    1 ~Test()
    2 {
    3     try{
    4         Finalize();
    5     }finally{
    6         base.Finalize();
    7     }
    8 }

    如果在派生类中不存在析造函数,却重载了基类的终结器:

    1 protected override void Finalize()
    2 {
    3 }

    垃圾回收时,GC找不到构造函数,会直接调用终结器。

    如果没有显示释放资源时,GC时可以靠该方法进行隐式的释放资源。

    Dispose

    相对于重写Finalize方法,实现IDisposable接口来进行显示的释放资源是更好的一种方式。

    我们只需要实现IDisposable接口即可,我们看看官网提供的常规写法:

     1 using System;
     2 
     3 class BaseClass : IDisposable
     4 {
     5    // Flag: Has Dispose already been called?
     6    bool disposed = false;
     7 
     8    // Public implementation of Dispose pattern callable by consumers.
     9    public void Dispose()
    10    { 
    11       Dispose(true);
    12       GC.SuppressFinalize(this);           
    13    }
    14 
    15    // Protected implementation of Dispose pattern.
    16    protected virtual void Dispose(bool disposing)
    17    {
    18       if (disposed)
    19          return; 
    20 
    21       if (disposing) {
    22          // Free any other managed objects here.
    23          //
    24       }
    25 
    26       // Free any unmanaged objects here.
    27       //
    28       disposed = true;
    29    }
    30 }

    https://msdn.microsoft.com/zh-cn/library/system.idisposable(v=vs.110).aspx

    https://msdn.microsoft.com/zh-cn/library/fs2xkftw(v=vs.110).aspx

    弱引用

    我们都知道当一个对象存在一个或多个引用时,GC是不会释放该对象的,如下:

    Object obj = new Object();

    这种引用称为强引用。

    但是我们可以想一下,还有一种情况是对象稍后可能被使用,但不是很确定是否会使用时,就可以使用弱引用了。

    弱引用可以理解为:如果一个对象只存在一个或多个弱引用而没有强引用时,则GC可以对其进行垃圾回收。

    那么在C#中该如何使用弱引用呢?

    WeakReference和WeakReference<T>

    C#提供了两个类来实现弱引用的功能,我们只需要将对象装入该类的实例中,则可以理解为为这个对象添加了一个弱引用,下面给出帮助文档地址:

    WeakReference:https://msdn.microsoft.com/zh-cn/library/system.weakreference(v=vs.110).aspx

    WeakReference<T>:https://msdn.microsoft.com/zh-cn/library/gg712738(v=vs.110).aspx

    我们再看一个例子:

     1 using System;
     2 
     3 namespace Study
     4 {
     5     class Program
     6     {
     7         static void Main(string[] args)
     8         {
     9             //强引用
    10             Test test = new Test();
    11             //弱引用 1
    12             WeakReference weak1 = new WeakReference(test);
    13             //弱引用 2
    14             WeakReference<Test> weak2 = new WeakReference<Test>(test);
    15 
    16             //存在强引用不会被回收
    17             GC.Collect();
    18 
    19             //注意 temp 也是一个强引用
    20             Test temp;
    21 
    22             Console.WriteLine("weak1: " + weak1.IsAlive + ", weak2: " + weak2.TryGetTarget(out temp));
    23 
    24             //解除所有强引用
    25             test = null;
    26             temp = null;
    27 
    28             //没有强引用会被回收
    29             GC.Collect();
    30 
    31             Console.WriteLine("weak1: " + weak1.IsAlive + ", weak2: " + weak2.TryGetTarget(out temp));
    32 
    33             Console.Read();
    34         }
    35     }
    36 
    37     public class Test
    38     {
    39         ~Test()
    40         {
    41             Console.WriteLine("Test被回收了!");
    42         }
    43     }
    44 }

    结果如下:

    1 weak1: True, weak2: True
    2 weak1: False, weak2: False
    3 Test被回收了!
  • 相关阅读:
    【codeforces 723F】stSpanning Tree
    struts2.0中struts.xml配置文件详解
    存储过程中调用JAVA程序段
    本不该逃避
    利用js实现对页面的自动刷新
    [转]从硬盘安装 RedHat Enterprise Linux Server 5 iso
    正则表达式使用
    利用XmlBean轻松读写xml(转)
    Struts2+Spring2+Hibernate3 web应用示例(七)
    在DWR中实现直接获取一个JAVA类的返回值的两种方法
  • 原文地址:https://www.cnblogs.com/hammerc/p/4616264.html
Copyright © 2020-2023  润新知