垃圾回收(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对象所在的代:
  2. test对象所在的代:
  3. test对象所在的代:
  4. test对象所在的代:
  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是不会释放该对象的,如下:

  1. 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被回收了!

C#学习笔记(十四):GC机制和弱引用的更多相关文章

  1. python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例

    python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例 新浪爱彩双色球开奖数据URL:http://zst.aicai.com/ssq/openInfo/ 最终输出结果格 ...

  2. (C/C++学习笔记) 十四. 动态分配

    十四. 动态分配 ● C语言实现动态数组 C语言实现动态数组,克服静态数组大小固定的缺陷 C语言中,数组长度必须在创建数组时指定,并且只能是一个常数,不能是变量.一旦定义了一个数组,系统将为它分配一个 ...

  3. SharpGL学习笔记(十四) 材质:十二个材质球

    材质颜色 OpenGL用材料对光的红.绿.蓝三原色的反射率来近似定义材料的颜色.象光源一样,材料颜色也分成环境.漫反射和镜面反射成分,它们决定了材料对环境光.漫反射光和镜面反射光的反射程度.在进行光照 ...

  4. 【转】angular学习笔记(十四)-$watch(1)

    本篇主要介绍$watch的基本概念: $watch是所有控制器的$scope中内置的方法: $scope.$watch(watchObj,watchCallback,ifDeep) watchObj: ...

  5. Java基础学习笔记十四 常用API之基本类型包装类

    基本类型包装类 Java中有8种基本的数据类型,可是这些数据是基本数据,想对其进行复杂操作,变的很难.怎么办呢?在实际程序使用中,程序界面上用户输入的数据都是以字符串类型进行存储的.而程序开发中,我们 ...

  6. angular学习笔记(十四)-$watch(1)

    本篇主要介绍$watch的基本概念: $watch是所有控制器的$scope中内置的方法: $scope.$watch(watchObj,watchCallback,ifDeep) watchObj: ...

  7. Java学习笔记十四:如何定义Java中的类以及使用对象的属性

    如何定义Java中的类以及使用对象的属性 一:类的重要性: 所有Java程序都以类class为组织单元: 二:什么是类: 类是模子,确定对象将会拥有的特征(属性)和行为(方法): 三:类的组成: 属性 ...

  8. MYSQL进阶学习笔记十四:MySQL 应用程序优化!(视频序号:进阶_32)

    知识点十五:MySQL 的应用程序优化(32) 一.访问数据库采用连接池 把连接当做对象或设备,统一放在‘连接池’里.凡是需要访问数据库的地方都从连接池里取连接 二.采用缓存减少对于MySQL的访问: ...

  9. Swift学习笔记十四:构造(Initialization)

         类和结构体在实例创建时,必须为全部存储型属性设置合适的初始值. 存储型属性的值不能处于一个未知的状态.     你能够在构造器中为存储型属性赋初值,也能够在定义属性时为其设置默认值.下面章节 ...

随机推荐

  1. bzoj2788

    明显是一个差分约束系统 对于第一种限制,其实就是x[a]+1<=x[b] x[b]-1<=x[a] 根据三角不等式很容易建图 但这题他比较奇怪,问的是X最多不同取值的个数 根据这张图的特殊 ...

  2. Java Servlet与Applet、CGI、JSP的比较

    Java Servlet是一种独立于平台和协议的服务器端的Java应用程序,可以生成动态的Web页面. Java Servlet是位于Web 服务器内部的服务器端的Java应用程序,与传统的从命令行启 ...

  3. 关于FastDFS Java客户端源码中的一个不太明白的地方

    下面代码是package org.csource.fastdfs下TrackerGroup.java文件中靠近结束的一段代码,我下载的这个源码的版本是1.24. /** * return connec ...

  4. Android-使用getIdentifier()获取资源Id

    使用getIdentifier()获取资源Id int i= getResources().getIdentifier("icon", "drawable", ...

  5. [转] arcgis Engine创建shp图层

    小生 原文 arcgis Engine创建shp图层 以创建点图层为例.首先要得到保存文件的地址. SaveFileDialog saveFileDialog = new SaveFileDialog ...

  6. Visual Studio 2010中创建ASP.Net Web Service

    转自:http://blog.csdn.net/xinyaping/article/details/7331375 很多人在论坛里说,在Visual Studio 2010中不能创建“ASP.Net ...

  7. CodeForces 148D-Bag of mice(概率dp)

    题意: 袋子里有w个白球b个黑球,现在两个人轮流每次取一个球(不放回),先取到白球的获胜,当后手取走一个球时,袋子里的球会随机的漏掉一个,问先手获胜的概率. 分析: dp[i][j]表示袋子中i个白球 ...

  8. Java连接Oracle10g

    1.导入驱动包: a.找到oracle安装目录下的jdbc/lib中的文件classes12.jar: b.右击你创建的JAVA工程,找到Build path,选择Add External Archi ...

  9. JS数组(Array)操作汇总

    1.去掉重复的数组元素.2.获取一个数组中的重复项.3.求一个字符串的字节长度,一个英文字符占用一个字节,一个中文字符占用两个字节.4.判断一个字符串中出现次数最多的字符,统计这个次数.5.数组排序. ...

  10. linux下安装pkg-config时遇到"glib-2.0>=2.16"的错

    解决办法 如报错提示所述,加上:--with-internal-glib 即 ./configure --with-internal-glib 参考链接: http://stackoverflow.c ...