C# 析构函数(Destructor)和终结器(Finalizer)——托管资源的释放

本文内容

  • 使用析构函数释放资源
  • Object.Finalize 方法
  • 资源的显式释放

 

使用析构函数释放资源


析构函数用于析构类的实例。

  • 不能在结构中定义析构函数。只能对类使用析构函数。
  • 一个类只能有一个析构函数。
  • 无法继承或重载析构函数。
  • 无法调用析构函数。它们是被自动调用的。
  • 析构函数既没有修饰符,也没有参数。

示例 1:类 Car 析构函数的声明。

class Car
{
    /// <summary>
    /// 析构函数
    /// </summary>
    ~Car()
    {
        // cleanup statements...     
    }
}

该析构函数隐式调用对象基类的 Finalize 方法。因此,该析构函数被隐式地转换为如下代码:

protected override void Finalize()  
{  
    try
    {
        // Cleanup statements...   
    }     
    finally 
    {
        base.Finalize();  
    }
} 

这意味着,对继承链中的所有实例递归调用 Finalize 方法。

说明:不要使用空的析构函数。如果类包含析构函数,则 Finalize  队列中则会创建一个项。当调用析构函数时,将调用垃圾回收器(GC)来处理该队列。如果析构函数为空,只会导致不必要的性能损失。

程序员无法控制何时调用析构函数,因为这由垃圾回收器决定。垃圾回收器检查是否存在应用程序不再使用的对象。如果垃圾回收器认为某个对象符合析构,则调用析构函数(如果有的话),回收该对象的内存。程序退出时也会调用析构函数。

可以通过调用 Collect 强制进行垃圾回收,但大多数情况下应避免这样做,因为会导致性能问题。

通常,.NET Framework 垃圾回收器会隐式地管理对象的内存分配和释放。但当应用程序封装窗口、文件

和网络连接这类非托管资源时,应使用析构函数释放这些资源。当对象符合析构时,垃圾回收器将运行对象的 Finalize 方法。

虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但它不了解具体如何清理这些资源。常见的非托管源有:ApplicationContext、Brush、Component、ComponentDesigner、Container、Context、Cursor、FileStream、Font、Icon、Image、Matrix、Object、OdbcDataReader、OleDBDataReader、Pen、Regex、Socket、StreamWriter、Timer、Tooltip 等。

 

Object.Finalize 方法


允许 Object 在“垃圾回收”回收 Object 之前,尝试释放资源并执行其他清理操作。Finalize 是受保护的,因此只能通过此类或派生类访问它。

对象变为不可访问后,将自动调用此方法,除非已通过  GC.SuppressFinalize 调用使对象免除了终结。在应用程序域的关闭过程中,对没有免除终结的对象将自动调用 Finalize,即使那些对象仍是可访问的。对于给定的实例仅自动调用 Finalize 一次,除非使用  GC.ReRegisterForFinalize重新注册该对象,并且后面没有调用 GC.SuppressFinalize。

派生类型中的每个 Finalize 实现都必须调用其基类型的 Finalize 实现。这是唯一一种允许应用程序代码调用 Finalize 的情况。

注意:C# 编译器不允许你直接实现 Finalize 方法,因此 C# 析构函数自动调用其基类的析构函数。

Finalize 操作具有下列限制:

1)   垃圾回收过程中执行终结器的准确时间是不确定的。不保证资源在任何特定的时间都能释放,除非调用 Close 方法或 Dispose 方法。

2)   即使一个对象引用另一个对象,也不能保证两个对象的终结器以任何特定的顺序运行。即,如果对象 A 具有对对象 B 的引用,并且两者都有终结器,则当对象 A 的终结器启动时,对象 B 可能已经终结了。

3)   运行终结器的线程是未指定的。

在下面的异常情况下,Finalize 方法可能不会运行完成或可能根本不运行:

1)   另一个终结器无限期地阻止(进入无限循环,试图获取永远无法获取的锁,诸如此类)。由于运行时试图运行终结器来完成,所以如果一个终结器无限期地阻止,则可能不会调用其他终结器。

2)   进程终止,但不给运行时提供清理的机会。在这种情况下,运行时的第一个进程终止通知是 DLL_PROCESS_DETACH 通知。

在关闭过程中,只有当可终结对象的数目继续减少时,运行时才继续 Finalize 对象。

如果 Finalize 或 Finalize 的重写引发异常,并且运行库并非寄宿在重写默认策略的应用程序中,则运行库将终止进程,并且不执行任何活动的 try-finally 块或终结器。如果终结器无法释放或销毁资源,此行为可以确保进程完整性。

说明:默认情况下,Object.Finalize 不执行任何操作。只有在必要时才必须由派生类重写它,因为如果必须运行 Finalize 操作,垃圾回收过程中的回收往往需要长得多的时间。如果 Object 保存了对任何资源的引用,则 Finalize 必须由派生类重写,以便在垃圾回收过程中,在放弃 Object 之前释放这些资源。当类型使用文件句柄或数据库连接这类在回收使用托管对象时必须释放的非托管资源时,该类型必须实现 Finalize。Finalize 可以采取任何操作,包括在垃圾回收过程中清理了对象后使对象复活(即,使对象再次可访问)。但是,对象只能复活一次;在垃圾回收过程中,不能对复活对象调用 Finalize。

析构函数是执行清理操作的 C# 机制。析构函数提供了适当的保护措施,如自动调用基类型的析构函数。在 C# 代码中,不能调用或重写 Object.Finalize。

示例 2:当一个重构 Finalize 方法的对象被销毁时,会调用 Finalize 方法。注意,在产品应用程序中,Finalize 方法会被重构,以便释放对象拥有的非托管资源。另外,C# 提供析构函数,而没有重构 Finalize 方法。

using System;
using System.Diagnostics;
 
public class ExampleClass
{
    Stopwatch sw;
    public ExampleClass()
    {
        sw = Stopwatch.StartNew();
        Console.WriteLine("Instantiated object");
    }
    public void ShowDuration()
    {
        Console.WriteLine("This instance of {0} has been in existence for {1}", this, sw.Elapsed);
    }
    ~ExampleClass()
    {
        Console.WriteLine("Finalizing object");
        sw.Stop();
        Console.WriteLine("This instance of {0} has been in existence for {1}", this, sw.Elapsed);
    }
}
public class Demo
{
    public static void Main()
    {
        ExampleClass ex = new ExampleClass();
        ex.ShowDuration();
    }
}

输出结果如下:

Instantiated object    
This instance of ExampleClass has been in existence for 00:00:00.0011060
Finalizing object
This instance of ExampleClass has been in existence for 00:00:00.0036294

 

资源的显式释放


若应用程序在使用昂贵的外部资源,建议提供一种在垃圾回收器释放对象前显式地释放资源的方式。可通过实现来自 IDisposable 接口的 Dispose 方法来完成这一点,该方法为对象执行必要的清理。这样可大大提高应用程序的性能。即使有这种对资源的显式控制,析构函数也是一种保护措施,可用来在对 Dispose 方法的调用失败时清理资源。

更多信息,请参见 清理非托管资源。

示例 3:创建三个类,这三个类构成了一个继承链。这三个类都有析构函数。在 Main() 中,创建了派生程度最大的类的实例。程序运行时,这三个类的析构函数将自动被调用,并按照从派生程度最大的到派生程度最小的次序调用。

class First
{
    ~First()
    {
        System.Diagnostics.Trace.WriteLine("First's destructor is called.");
    }
}
class Second : First
{
    ~Second()
    {
        System.Diagnostics.Trace.WriteLine("Second's destructor is called.");
    }
}
class Third : Second
{
    ~Third()
    {
        System.Diagnostics.Trace.WriteLine("Third's destructor is called.");
    }
}
class TestDestructors
{
    static void Main()
    {
        Third t = new Third();
    }
}

在VS 输出窗口(快捷键Ctrl+W,O)会看到如下信息:

……
Third's destructor is called.
Second's destructor is called.
First's destructor is called.
程序“[3796] TestDestructors.vshost.exe: 托管”已退出,返回值为0 (0x0)。

更多信息,请参见 C# 语言规范中的以下各章节:

a) 1.6.7.6 析构函数

b) 10.3.9.4 为析构函数保留的成员名称

c)  10.13 析构函数(类)

d)  11.3.9 析构函数(结构)

C# 语言规范位于 Visual Studio 2008 安装目录下的 VC#/Specifications/1033/ 目录。

 

参考


GC http://msdn.microsoft.com/zh-cn/library/0xy59wtx(v=VS.90).aspx

o_r_%E7%BF%BB%E8%AF%91.jpg

下载 Demo

转载于:https://www.cnblogs.com/liuning8023/archive/2012/07/22/2603819.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/494869.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

教育部:建设100+AI特色专业, 500万AI人才缺口要补上!

来源&#xff1a;网络大数据摘要&#xff1a;为落实《国务院关于印发新一代人工智能发展规划的通知》&#xff0c;为我国新一代人工智能发展提供战略支撑&#xff0c;教育部在近日正式发布了《高等学校人工智能创新行动计划》。AI再次被提上国家级日程!为落实《国务院关于印发新…

TCP三次握手详解及面试题

为什么必须是三次握手&#xff1f; 大家都知道传输层&#xff08;点击这里去传输层&#xff09;中的TCP协议是面向连接的&#xff0c;提供可靠的连接服务&#xff0c;其中最出名的就是三次握手和四次挥手&#xff0c;今天先讲解三次握手&#xff08;四次挥手点这里&#xff09;…

git 撤销修改:未push 、已push

场景&#xff1a;不小心把一次错误的代码push到远程服务器上的分支上&#xff0c;需要立即删除/撤销这次代码提交。 具体方法&#xff0c;git命令&#xff1a; git loggit reset --hard <commit_id>git push origin HEAD --force【命令详解】 获取commit_id&#xff1…

一条光纤的传输容量高达 661Tbps(附论文)

来源&#xff1a;云头条摘要&#xff1a;研究人员将全世界目前的光纤容量塞入到一条链路中。社会对数据的渴求永无止境。事实上&#xff0c;想想这个就令人相当惊讶&#xff1a;平均的互联网流量是每秒几百兆兆位&#xff0c;耗电量约占我们发电量的8%。这一切用来传输猫咪即时…

tcp四次挥手,为什么是四次?

上一篇博客说了三次握手为什么是是三次&#xff08;点这里&#xff09;&#xff0c;那么现在就介绍一下四次挥手。大家都知道TCP是全双工的&#xff0c;再建立连接时的三次握手中的SYN和ACK一起发送&#xff0c;这里就会有疑问&#xff0c;为什么在四次挥手的时候没有将SYN和AC…

市值破万亿美金的苹果 近年在AI上搞了些新动作

来源&#xff1a;网易智能2007年&#xff0c;中石油在中国A股市场上市首日市值曾达到1.1万亿美元。11年之后&#xff0c;当地时间8月2日&#xff0c;苹果公司盘中市值也超过1万亿美金&#xff0c;成为继中石油后的第二家破万亿公司。苹果股价以207.39美元收盘&#xff0c;涨幅为…

网络层(学习笔记)

网络层 负责地址管理与路由选择&#xff0c;在复杂的通信环境中 IP协议 IP协议头格式 4位协议版本&#xff1a;IPV4/IPV6 4位头部长度&#xff1a;表示ip头有多长&#xff0c;最长60字节&#xff0c;最小20字节 8位服务类型&#xff1a;TOS字段&#xff08;最小延时&#xff…

jQuery 的各种练习

这个星期最大的感悟是&#xff0c;只有在实践中不断的总结&#xff0c;才能打下扎实基本功。这是本周主要做的东西&#xff1a; 第一个图主要为对jQuery ajax的练习。后面两个计算器和新浪微博页面为之前做好的页面&#xff0c;这次用一个load()函数把它们加载进来。不过中间的…

蜜糖变砒霜:90%美国公司区块链项目将不再重启

来源&#xff1a;雷锋网摘要&#xff1a;市场对区块链的“迷恋”来得轰轰烈烈&#xff0c;退得悄无声息。一方面是科技巨头占山为王&#xff0c;另一方面不少此前号称投入研发区块链的公司已经把目光收回&#xff0c;并且表示再也不会重启这些试点项目。有人觉得区块链就此沉寂…

【埋点】是什么埋点?简述埋点的操作流程

埋点&#xff1a;又称为事件追踪&#xff08;Event Tracking&#xff09;&#xff0c;指的是针对特定用户行为或事件进行捕获&#xff0c;处理和发送的相关技术及其实施过程。 功能方面&#xff1a;埋点是用来收集用户行为数据。比如想要了解一个用户在APP里面点击了哪些按钮&…

【转】如何让ucgui支持24位色(24bpp)

将UC/GUI 3.32a 更改为可以支持24bpp色彩模式 我打算在UC/OS-II上直接移植一个开源的GUI界面。所以我从网上找了一些GUI的开源代码&#xff0c;主要看了看飞漫软件的MiniGUI和Micrium公司的uC/GUI。 飞漫软件的MiniGUI可免费下载的版本是1.3.3&#xff0c;可是不支持UC/OS-II&a…

NASA指定首批9名宇航员,参与波音和马斯克商业载人航天

来源&#xff1a;澎湃新闻人类太空探索史即将翻开商业载人航天的新一页。美国当地时间8月3日&#xff0c;美国国家航空航天局&#xff08;NASA&#xff09;公布了9名将搭乘波音公司的CST-100 Starliner载人航天器和SpaceX载人龙飞船往返国际空间站的宇航员。其中3名宇航员参与S…

二叉树——基本概念

二叉树的概念 树是一种非线性的数据结构&#xff0c;他有n(n>0)个有限的结点组成的一个有层次关系的集合。之所以叫树&#xff0c;是因为这种数据结构看起来像是一个倒挂的树&#xff0c;根朝上&#xff0c;叶子朝下。特点就是每个结点有0个或多个结点&#xff0c;没有父结点…

设计模式第三集——装饰者模式(Decorator)

再次强调设计的重要原则&#xff1a;对扩展开放&#xff0c;对修改关闭。在设计中要尽量避免对之前源代码的修改。 为适应扩展的特性&#xff0c;除了继承之外&#xff0c;还可以用装饰者模式&#xff1a;动态的将新的功能附加到对象上。换句话说&#xff0c;装饰者模式就是有一…

AI开放只是幌子?科技巨头边承诺开放边申请专利

选自 I Wired编译 I 网易智能参与 I 木秀林据《连线》报道&#xff0c;上周在旧金山举行的谷歌云计算会议上&#xff0c;该公司CEO桑德尔皮查伊提到公司致力于人工智能&#xff08;AI&#xff09;的开放。他表示&#xff1a;“我们创立开放的平台&#xff0c;分享我们的技术&am…

两种列式存储格式:Parquet和ORC

背景 随着大数据时代的到来&#xff0c;越来越多的数据流向了Hadoop生态圈&#xff0c;同时对于能够快速的从TB甚至PB级别的数据中获取有价值的数据对于一个产品和公司来说更加重要&#xff0c;在Hadoop生态圈的快速发展过程中&#xff0c;涌现了一批开源的数据分析引擎&#…

数据链路层(学习笔记)

首先要明确“数据链路”和“链路”这两个概念&#xff1a; 链路&#xff1a;从一个节点到相邻节点的一段物理现路&#xff0c;其中间没有任何的交换节点&#xff0c;所以可以说链路只是一条路径的组成部分。   数据链路&#xff1a;当需要在一条线路上传送数据的时候&#xf…

免otp动态密码登录堡垒机

环境准备 安装brew 参考文档&#xff1a;https://brew.sh/index_zh-cn.html 安装oath-toolkit 和expect brew install oath-toolkit brew install expect 正式开始 生成MFA_KEY对应的6位otp密码&#xff1a;oathtool -b --totp [MFA_KEY] MFA_KEY就是你绑定APP时&#x…