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代,清理方式如上相同。
我们来看一个例子:
using System; namespace Study
{
class Program
{
static void Main(string[] args)
{
Test test = new Test(); //对象会被分配到第 0 代
Console.WriteLine("test对象所在的代:" + GC.GetGeneration(test));
//回收对象, 这时 test 会被分配到第 1 代
GC.Collect();
Console.WriteLine("test对象所在的代:" + GC.GetGeneration(test));
//回收对象, 这时 test 会被分配到第 2 代
GC.Collect();
Console.WriteLine("test对象所在的代:" + GC.GetGeneration(test));
//回收对象, 最多只有3个代, 所以 test 还在第 2 代
GC.Collect();
Console.WriteLine("test对象所在的代:" + GC.GetGeneration(test)); //断开引用, 对象会被回收
test = null;
//回收对象, test 会被回收并调用析构函数
GC.Collect(); Console.Read();
}
} public class Test
{
~Test()
{
Console.WriteLine("Test被回收了!");
}
}
}
运行结果如下:
test对象所在的代:
test对象所在的代:
test对象所在的代:
test对象所在的代:
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
当我们声明一个析构函数时实际上编译器就会自动添加下面的代码:
~Test()
{
try{
Finalize();
}finally{
base.Finalize();
}
}
如果在派生类中不存在析造函数,却重载了基类的终结器:
protected override void Finalize()
{
}
垃圾回收时,GC找不到构造函数,会直接调用终结器。
如果没有显示释放资源时,GC时可以靠该方法进行隐式的释放资源。
Dispose
相对于重写Finalize方法,实现IDisposable接口来进行显示的释放资源是更好的一种方式。
我们只需要实现IDisposable接口即可,我们看看官网提供的常规写法:
using System; class BaseClass : IDisposable
{
// Flag: Has Dispose already been called?
bool disposed = false; // Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
} // Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return; if (disposing) {
// Free any other managed objects here.
//
} // Free any unmanaged objects here.
//
disposed = true;
}
}
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
我们再看一个例子:
using System; namespace Study
{
class Program
{
static void Main(string[] args)
{
//强引用
Test test = new Test();
//弱引用 1
WeakReference weak1 = new WeakReference(test);
//弱引用 2
WeakReference<Test> weak2 = new WeakReference<Test>(test); //存在强引用不会被回收
GC.Collect(); //注意 temp 也是一个强引用
Test temp; Console.WriteLine("weak1: " + weak1.IsAlive + ", weak2: " + weak2.TryGetTarget(out temp)); //解除所有强引用
test = null;
temp = null; //没有强引用会被回收
GC.Collect(); Console.WriteLine("weak1: " + weak1.IsAlive + ", weak2: " + weak2.TryGetTarget(out temp)); Console.Read();
}
} public class Test
{
~Test()
{
Console.WriteLine("Test被回收了!");
}
}
}
结果如下:
weak1: True, weak2: True
weak1: False, weak2: False
Test被回收了!
C#学习笔记(十四):GC机制和弱引用的更多相关文章
- python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例
python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例 新浪爱彩双色球开奖数据URL:http://zst.aicai.com/ssq/openInfo/ 最终输出结果格 ...
- (C/C++学习笔记) 十四. 动态分配
十四. 动态分配 ● C语言实现动态数组 C语言实现动态数组,克服静态数组大小固定的缺陷 C语言中,数组长度必须在创建数组时指定,并且只能是一个常数,不能是变量.一旦定义了一个数组,系统将为它分配一个 ...
- SharpGL学习笔记(十四) 材质:十二个材质球
材质颜色 OpenGL用材料对光的红.绿.蓝三原色的反射率来近似定义材料的颜色.象光源一样,材料颜色也分成环境.漫反射和镜面反射成分,它们决定了材料对环境光.漫反射光和镜面反射光的反射程度.在进行光照 ...
- 【转】angular学习笔记(十四)-$watch(1)
本篇主要介绍$watch的基本概念: $watch是所有控制器的$scope中内置的方法: $scope.$watch(watchObj,watchCallback,ifDeep) watchObj: ...
- Java基础学习笔记十四 常用API之基本类型包装类
基本类型包装类 Java中有8种基本的数据类型,可是这些数据是基本数据,想对其进行复杂操作,变的很难.怎么办呢?在实际程序使用中,程序界面上用户输入的数据都是以字符串类型进行存储的.而程序开发中,我们 ...
- angular学习笔记(十四)-$watch(1)
本篇主要介绍$watch的基本概念: $watch是所有控制器的$scope中内置的方法: $scope.$watch(watchObj,watchCallback,ifDeep) watchObj: ...
- Java学习笔记十四:如何定义Java中的类以及使用对象的属性
如何定义Java中的类以及使用对象的属性 一:类的重要性: 所有Java程序都以类class为组织单元: 二:什么是类: 类是模子,确定对象将会拥有的特征(属性)和行为(方法): 三:类的组成: 属性 ...
- MYSQL进阶学习笔记十四:MySQL 应用程序优化!(视频序号:进阶_32)
知识点十五:MySQL 的应用程序优化(32) 一.访问数据库采用连接池 把连接当做对象或设备,统一放在‘连接池’里.凡是需要访问数据库的地方都从连接池里取连接 二.采用缓存减少对于MySQL的访问: ...
- Swift学习笔记十四:构造(Initialization)
类和结构体在实例创建时,必须为全部存储型属性设置合适的初始值. 存储型属性的值不能处于一个未知的状态. 你能够在构造器中为存储型属性赋初值,也能够在定义属性时为其设置默认值.下面章节 ...
随机推荐
- UVa 1349 (二分图最小权完美匹配) Optimal Bus Route Design
题意: 给出一个有向带权图,找到若干个圈,使得每个点恰好属于一个圈.而且这些圈所有边的权值之和最小. 分析: 每个点恰好属于一个有向圈 就等价于 每个点都有唯一后继. 所以把每个点i拆成两个点,Xi ...
- ionic安装拍照选照片插件
1.安装插件,也可以用ionic plugin add .... phonegap local plugin add https://git-wip-us.apache.org/repos/asf/c ...
- AngularJS 拦截器和应用例子(转)
$httpAngularJS 的 $http 服务允许我们通过发送 HTTP 请求方式与后台进行通信.在某些情况下,我们希望可以俘获所有的请求,并且在将其发送到服务端之前进行操作.还有一些情况是,我们 ...
- Cocoa Touch(一)开发基础:Xcode概念、目录结构、设计模式、代码风格
Xcode相关概念: 概念:project 指一个项目,该项目会负责管理软件产品的全部源代码文件.全部资源文件.相关配置,一个Project可以包含多个Target. 概念:target 一个targ ...
- 自己动手写路由器之ioctl获取网络接口信息
最近打算写一个简单路由器,里面有用到ioctl获取网络接口信息,那就先把这部分单独拿出来说一说吧! ioctl这个函数,可以用来对特殊文件的基础设备参数进行操作,它们可以完成与打开文件描述符相关联的控 ...
- 你所不知道的 URL
0.说明 第一幕 产品:大叔有用户反映账户不能绑定公众号.大叔:啊咧咧?怎么可能,我看看?大叔:恩?这也没问题啊,魏虾米.大叔:还是没问题啊,挖叉类.大叔:T T,话说产品姐姐是不是Java提供接口的 ...
- hdu 3172 Virtual Friends(并查集)University of Waterloo Local Contest 2008.09
题目比较简单,但作为长久不写题之后的热身题还是不错的. 统计每组朋友的朋友圈的大小. 如果a和b是朋友,这个朋友圈的大小为2,如果b和c也是朋友,那么a和c也是朋友,此时这个朋友圈的大小为3. 输入t ...
- Mondriaan's Dream(POJ 2411状态压缩dp)
题意:用1*2的方格填充m*n的方格不能重叠,问有多少种填充方法 分析:dp[i][j]表示i行状态为j时的方案数,对于j,0表示该列竖放(影响下一行的该列),1表示横放成功(影响下一列)或上一列竖放 ...
- 《Python 学习手册4th》 第十六章 函数基础
''' 时间: 9月5日 - 9月30日 要求: 1. 书本内容总结归纳,整理在博客园笔记上传 2. 完成所有课后习题 注:“#” 后加的是备注内容 (每天看42页内容,可以保证月底看完此书) “重点 ...
- "_ITERATOR_DEBUG_LEVEL"的不匹配项: 值"0"不匹配值"2"
error: 1>vtkCommon.lib(vtkDebugLeaksManager.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项 ...