博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#学习笔记(十四):GC机制和弱引用
阅读量:6482 次
发布时间:2019-06-23

本文共 4642 字,大约阅读时间需要 15 分钟。

垃圾回收(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 Test33     {34         ~Test()35         {36             Console.WriteLine("Test被回收了!");37         }38     }39 }

运行结果如下:

1 test对象所在的代:02 test对象所在的代:13 test对象所在的代:24 test对象所在的代:25 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 }

弱引用

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

Object obj = new Object();

这种引用称为强引用。

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

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

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

WeakReference和WeakReference<T>

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

WeakReference:

WeakReference<T>:

我们再看一个例子:

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             //弱引用 112             WeakReference weak1 = new WeakReference(test);13             //弱引用 214             WeakReference
weak2 = new WeakReference
(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 Test38 {39 ~Test()40 {41 Console.WriteLine("Test被回收了!");42 }43 }44 }

结果如下:

1 weak1: True, weak2: True2 weak1: False, weak2: False3 Test被回收了!

 

转载地址:http://tbfuo.baihongyu.com/

你可能感兴趣的文章
Linux学习之常用网络通信命令与shell简单应用技巧(四)
查看>>
Python-递归实现
查看>>
webbench压力性能测试
查看>>
java StrutsTypeConverter的使用
查看>>
取出重复的客运车班次,两个字段的值互换视为重复值
查看>>
n!素因子p的幂 swjtuOJ 2090【数论】
查看>>
UT-Exynos4412 三星ARM四核旗舰开发平台android4.0体验-13串口功能调试
查看>>
设计模式状态
查看>>
day44-Celery异步分布式
查看>>
CentOS(5.8/6.4)linux生产环境若干优化实战
查看>>
iOS开发那些事--编写OCUnit测试方法-应用测试方法
查看>>
演示:带时间ACL的配置
查看>>
1.05.体验-Cisco UC-基本功能 v1.1(请-下载-附件(百度云盘)! 有惊喜!)
查看>>
Setting Up Flume High Availability
查看>>
SQL语句的MINUS,INTERSECT和UNION ALL
查看>>
hbase和zookeeper的安装和部署
查看>>
[原]运行编译好的Android模拟器
查看>>
StringBuilder、StringBuffer和String三者的联系和区别(转)
查看>>
快播遭版权清算,小米为何“兔死狐悲”?
查看>>
Html+jquery mobile
查看>>