转自:http://blog.csdn.net/sykpboy/article/details/342971
http://www.cnblogs.com/lzh/archive/2007/07/18/822388.html
http://blog.sina.com.cn/s/blog_740c17ab0100p2gw.html
便于对文章的开展,需要先明确两个概念。
第一个就是很多人用.Net写程序,会谈到托管这个概念。那么.Net所指的资源托管到底是什么意思,是相对于所有资源,还是只限于某一方面资源?很多人对此不是很了解,其实.Net所指的托管只是针对内存这一个方面,并不是对于所有的资源;因此对于Stream,数据库的连接,GDI+的相关对象,还有Com对象等等,这些资源并不是受到.Net管理而统称为非托管资源。而对于内存的释放和回收,系统提供了GC-Garbage Collector,而至于其他资源则需要手动进行释放。
那么第二个概念就是什么是垃圾,通过我以前的文章,会了解到.Net类型分为两大类,一个就是值类型,另一个就是引用类型。前者是分配在栈上,并不需要GC回收;后者是分配在堆上,因此它的内存释放和回收需要通过GC来完成。GC的全称为“Garbage Collector”,顾名思义就是垃圾回收器,那么只有被称为垃圾的对象才能被GC回收。也就是说,一个引用类型对象所占用的内存需要被GC回收,需要先成为垃圾。那么.Net如何判定一个引用类型对象是垃圾呢,.Net的判断很简单,只要判定此对象或者其包含的子对象没有任何引用是有效的,那么系统就认为它是垃圾。
明确了这两个基本概念,接下来说说GC的运作方式以及其的功能。内存的释放和回收需要伴随着程序的运行,因此系统为GC安排了独立的线程。那么GC的工作大致是,查询内存中对象是否成为垃圾,然后对垃圾进行释放和回收。那么对于GC对于内存回收采取了一定的优先算法进行轮循回收内存资源。其次,对于内存中的垃圾分为两种,一种是需要调用对象的析构函数,另一种是不需要调用的。GC对于前者的回收需要通过两步完成,第一步是调用对象的析构函数,第二步是回收内存,但是要注意这两步不是在GC一次轮循完成,即需要两次轮循;相对于后者,则只是回收内存而已。
很明显得知,对于某个具体的资源,无法确切知道,对象析构函数什么时候被调用,以及GC什么时候会去释放和回收它所占用的内存。那么对于从C、C++之类语言转换过来的程序员来说,这里需要转变观念。
那么对于程序资源来说,我们应该做些什么,以及如何去做,才能使程序效率最高,同时占用资源能尽快的释放。前面也说了,资源分为两种,托管的内存资源,这是不需要我们操心的,系统已经为我们进行管理了;那么对于非托管的资源,这里再重申一下,就是Stream,数据库的连接,GDI+的相关对象,还有Com对象等等这些资源,需要我们手动去释放。
如何去释放,应该把这些操作放到哪里比较好呢。.Net提供了三种方法,也是最常见的三种,大致如下:
<!--[if !supportLists]-->1. <!--[endif]-->析构函数;
<!--[if !supportLists]-->2. <!--[endif]-->继承IDisposable接口,实现Dispose方法;
<!--[if !supportLists]-->3. <!--[endif]-->提供Close方法。
经过前面的介绍,可以知道析构函数只能被GC来调用的,那么无法确定它什么时候被调用,因此用它作为资源的释放并不是很合理,因为资源释放不及时;但是为了防止资源泄漏,毕竟它会被GC调用,因此析构函数可以作为一个补救方法。而Close与Dispose这两种方法的区别在于,调用完了对象的Close方法后,此对象有可能被重新进行使用;而Dispose方法来说,此对象所占有的资源需要被标记为无用了,也就是此对象被销毁了,不能再被使用。例如,常见SqlConnection这个类,当调用完Close方法后,可以通过Open重新打开数据库连接,当彻底不用这个对象了就可以调用Dispose方法来标记此对象无用,等待GC回收。明白了这两种方法的意思后,大家在往自己的类中添加的接口时候,不要歪曲了这两者意思。
接下来说说这三个函数的调用时机,我用几个试验结果来进行说明,可能会使大家的印象更深。
首先是这三种方法的实现,大致如下:
///<summary> /// The class to show three disposal function ///</summary> public class DisposeClass:IDisposable { public void Close() { Debug.WriteLine( "Close called!" ); } ~DisposeClass() { Debug.WriteLine( "Destructor called!" ); } #region IDisposable Members public void Dispose() { // TODO: Add DisposeClass.Dispose implementation Debug.WriteLine( "Dispose called!" ); } #endregion }
对于Close来说不属于真正意义上的释放,除了注意它需要显示被调用外,我在此对它不多说了。而对于析构函数而言,不是在对象离开作用域后立刻被执行,只有在关闭进程或者调用GC.Collect方法的时候才被调用,参看如下的代码运行结果。
private void Create() { DisposeClass myClass = new DisposeClass(); } private void CallGC() { GC.Collect(); } // Show destructor Create(); Debug.WriteLine( "After created!" ); CallGC();
运行的结果为:
After created!
Destructor called!
显然在出了Create函数外,myClass对象的析构函数没有被立刻调用,而是等显示调用GC.Collect才被调用。
对于Dispose来说,也需要显示的调用,但是对于继承了IDisposable的类型对象可以使用using这个关键字,这样对象的Dispose方法在出了using范围后会被自动调用。例如:
using( DisposeClass myClass = new DisposeClass() )
{
//other operation here
}
如上运行的结果如下:
Dispose called!
那么对于如上DisposeClass类型的Dispose实现来说,事实上GC还需要调用对象的析构函数,按照前面的GC流程来说,GC对于需要调用析构函数的对象来说,至少经过两个步骤,即首先调用对象的析构函数,其次回收内存。也就是说,按照上面所写的Dispose函数,虽说被执行了,但是GC还是需要执行析构函数,那么一个完整的Dispose函数,应该通过调用GC.SuppressFinalize(this )来告诉GC,让它不用再调用对象的析构函数中。那么改写后的DisposeClass如下:
///<summary> /// The class to show three disposal function ///</summary> public class DisposeClass:IDisposable { public void Close() { Debug.WriteLine( "Close called!" ); } ~DisposeClass() { Debug.WriteLine( "Destructor called!" ); } #region IDisposable Members public void Dispose() { // TODO: Add DisposeClass.Dispose implementation Debug.WriteLine( "Dispose called!" ); GC.SuppressFinalize( this ); } #endregion }
通过如下的代码进行测试。
private void Run() { using( DisposeClass myClass = new DisposeClass() ) { //other operation here } } private void CallGC() { GC.Collect(); } // Show destructor Run(); Debug.WriteLine( "After Run!" ); CallGC();
运行的结果如下:
Dispose called!
After Run!
显然对象的析构函数没有被调用。通过如上的实验以及文字说明,大家会得到如下的一个对比表格。
======================================================================
object objA = new object(); object objB = objA; objA = null; // 强迫回收。 GC.Collect(); objB.ToString();这里objA引用的对象并没有被回收,因为这个对象还有另一个引用,ObjB。
public class CountObject { public static int Count = 0; public CountObject() { Count++; } ~CountObject() { Count--; } } static void Main() { CountObject obj; for (int i = 0; i < 5; i++) { obj = null; // 这一步多余,这么写只是为了更清晰些! obj = new CountObject(); } // Count不会是1,因为Finalizer不会马上被触发,要等到有一次回收操作后才会被触发。 Console.WriteLine(CountObject.Count); Console.ReadLine(); }注意以上代码要是改用C++写的话会发生内存泄漏,因为我们没有用delete操作符手动清理内存,但是在托管代码中却不会发生内存泄漏,因为GC会自动检测没有引用了的对象并回收。这里作者推荐你只在实现IDisposable接口时配合使用Finalizer,在其他的情况下不要使用(可能会有特殊情况)。在非托管资源的释放一章我们会更好的了解IDisposable接口,现在让我们来做耶稣吧!
public class Resurrection { public int Data; public Resurrection(int data) { this.Data = data; } ~Resurrection() { Main.Instance = this; } } public class Main { public static Resurrection Instance; public static void Main() { Instance = new Resurrection(1); Instance = null; GC.Collect(); GC.WaitForPendingFinalizers(); // 看到了吗,在这里“复活”了。 Console.WriteLine(Instance.Data); Instance = null; GC.Collect(); Console.ReadLine(); } }你可能会问:"既然这个对象能复活,那么这个对象在程序结束后会被回收吗?"。会,"为什么?"。让我们按照GC的工作方式走一遍你就明白是怎么回事了。 1、执行Collect。检查引用。没问题,对象已经没有引用了。
public class Base : IDisposable { public void Dispose() { this.Dispose(true); GC.SupressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { // 托管类 } // 非托管资源释放 } ~Base() { this.Dispose(false); } } public class Derive : Base { protected override void Dispose(bool disposing) { if (disposing) { // 托管类 } // 非托管资源释放 base.Dispose(disposing); } }为什么要这样设计呢?让我在后面解说一下。现在我们讲讲实现这个Dispose方法的几个准则:
public class Fat { public int Data; public Fat(int data) { this.Data = data; } } public class Main { public static void Main() { Fat oFat = new Fat(1); WeakReference oFatRef = new WeakReference(oFat); // 从这里开始,Fat对象可以被回收了。 oFat = null; if (oFatRef.IsAlive) { Console.WriteLine(((Fat) oFatRef.Target).Data); // 1 } // 强制回收。 GC.Collect(); Console.WriteLine(oFatRef.IsAlive); // False Console.ReadLine(); } }
相关推荐
《GC5025.tar.gz:RK3368平台上的GC5025摄像头驱动详解》 在嵌入式系统开发中,摄像头驱动扮演着至关重要的角色,它连接硬件设备与软件应用,使得图像捕获和处理成为可能。本文将深入探讨针对RK3368平台的GC5025...
【标题】"gc2355_mipi_raw_摄像头gc2355驱动_" 指的是一个针对特定型号摄像头GC2355的驱动程序,该驱动是为MT6737处理器平台设计的。在嵌入式系统中,摄像头驱动是连接硬件设备(即摄像头)与操作系统内核之间的重要...
图纸转换工具GC2000是一款专为PCB布局工程师设计的强大GERBER文件查看软件,其功能强大,操作简便,是电子设计领域不可或缺的辅助工具。GC2000不仅能够帮助用户高效地查看和理解复杂的电路板设计,还支持多种格式的...
### GC0329 CMOS 图像传感器技术详解 #### 一、传感器概述 ##### 1.1 一般描述 GC0329是一款由国内最大的CMOS图像传感器制造商格科微电子研发的新一代VGA图像传感器。该传感器拥有640×480像素的分辨率,采用1/9...
这份教程主要讲解了如何使用GC软件来转换和对齐CAD文件与Gerber文件,以便于在电路板设计中确保精确的布局。以下是详细步骤及相关的知识点: 1. **导入Gerber文件** - 在GC软件中,通过菜单栏选择`File` > `Import...
根据给定的文件信息,以下是关于GC0339图像传感器的数据手册中提到的技术知识点详细说明: 标题解析:“gc0339datasheet”指的是GC0339图像传感器的数据手册。数据手册是详细记录产品规格、技术参数、引脚描述、...
根据提供的文档内容,我们可以梳理出关于GC4663数据手册的相关知识点如下: 1. 传感器概述: 1.1 通用描述: GC4663是一款高质量的400万像素CMOS图像传感器,适用于安全摄像头、数码相机产品以及手机摄像头应用。该...
GC0328是一款由Galaxy Core公司生产的1/6.5英寸VGACMOS图像传感器,具有高分辨率和良好的成像性能,特别适用于移动设备和电子产品的摄像头应用。 传感器概述: GC0328拥有640x480像素的分辨率和1/6.5英寸的光学格式...
GC-GERBER转CAD坐标.-smt 离线编程可以学习哦
标题中的“1.28寸的GC9a01屏幕资料”指的是一个1.28英寸大小的显示屏,型号为GC9a01。这种屏幕通常用于小型电子设备,如智能手表、便携式仪器或物联网设备的显示界面。GC9a01是一款基于SPI(Serial Peripheral ...
《GC-PowerStation5.44:SMT生产的GERBER与坐标转换利器详解》 在电子制造领域,尤其是表面贴装技术(Surface Mount Technology,简称SMT)中,精确的元器件布局和焊接至关重要。GC-PowerStation5.44是一款专业级的...
《GC9503V-DS IC规格书》是一份重要的技术文档,主要面向嵌入式开发领域的工程师,尤其在进行点屏操作或编写显示驱动程序时,此规格书是不可或缺的参考资料。嵌入式系统通常涉及到硬件和软件的紧密集成,其中显示...
标题中的"3516EV300+GC2053.zip"表明这是一个与HI3516EV300芯片和GC2053传感器相关的软件或驱动程序包。描述中提到"GC2053用于HI3518EV300HI3516EV200//HI3516EV300/ LITEOS 的驱动",这暗示GC2053是针对这三个不同...
**GC2093 CSP Datasheet Beta0.3 .pdf** 文件主要介绍了一款由格科微(Galaxycore)公司推出的高动态范围(HDR)1080P CMOS图像传感器——GC2093。这款传感器设计用于安全摄像头、数码相机以及手机摄像头应用。以下...
GC9A01则是一款专用于LCD显示的驱动芯片,常用于小型彩色显示屏,如圆形屏幕。这个项目是关于如何使用ESP32驱动GC9A01芯片来在圆形屏幕上显示图片,并且能根据预设时间自动更换显示内容,增加互动性和趣味性。 首先...
标题 "GC-Prevue官方原版" 暗示我们讨论的是一个专为电子制造行业设计的软件,用于查看和处理Gerber文件。Gerber文件是PCB(印制电路板)制造过程中的标准格式,它包含了PCB设计的各个层面的详细信息,如导电路径、...
《GC-Place软件及使用教程》是一份详细指导用户如何安装和操作GC-Place软件的资源包,旨在帮助用户快速掌握这款专用于SMT(Surface Mount Technology)贴片机编程的工具。GC-Place在电子制造领域扮演着重要的角色,...
《GC2145 MIPI驱动详解:在MTK Android 4.4系统中的应用与配置》 在Android操作系统中,驱动程序是连接硬件设备与操作系统内核的关键桥梁,它们负责管理和优化硬件资源,使软件应用程序能够充分利用硬件功能。本文...
### GC9307TFT显示屏驱动IC数据手册知识点总结 #### 一、GC9307概述 GC9307是一款专为a-Si TFT LCD设计的单芯片驱动器,支持240RGBx320分辨率及262K色显示,适用于多种小型至中型尺寸的触摸屏应用。此驱动IC旨在...
【GC7107CQ电压表原理图PCB源文件】是一个与电子工程相关的主题,特别是关于电压测量设备的设计。GC7107CQ是一款集成电路,常用于电压检测和显示电路中,可能是数字电压表的核心部分。下面将详细讨论相关知识点。 1...