毁灭的不确定性

发布日期:2026-07-14 18:55:03   来源 : 杭州电子商务研究院    浏览量 :6
杭州电子商务研究院 发布日期:2026-07-14 18:55:03  
6

介绍

析构函数是一个类似于构造函数的概念。构造函数的目的是为特定类的对象保留资源。析构函数的目的是释放这些资源。析构函数与垃圾收集器紧密相关。垃圾收集器是 C# 的内部机制,其唯一目的是释放那些不再需要但曾经由应用程序本身保留的资源。

析构函数也称为终结器。

垃圾收集器

在 C# 中创建对象意味着 CLR(公共语言运行时)从堆中分配内存供对象使用。每次连续创建对象时都会重复此操作。理论上,限制是系统中的可用内存。这意味着存在限制,我们需要考虑可能需要使用这些资源的其他应用程序。当应用程序不再需要内存时,需要偶尔释放内存。垃圾收集器的唯一职责是分配和回收这种宝贵的资源。

当创建新进程时,会为其分配一个单独的虚拟空间。这来自物理内存,系统中的所有其他进程都会使用它。每个程序都在使用虚拟空间,并且与物理内存没有直接交互。垃圾收集器正在处理同一虚拟内存以分配和回收内存。

虚拟内存中存在三个块:

  1. 免费 - 空白空间
  2. 保留 - 已分配空间
  3. 已提交 - 这是物理内存的分配,不能用于空间分配

一旦虚拟内存耗尽,就会发生内存不足错误。

有一个概念叫做“代”,它向我们揭示了垃圾收集的内部工作原理。垃圾收集分为三代,将不同类型的对象分为不同的类别

生成类别:

  1. 代(零) - 保存短期对象、临时对象;垃圾收集器是这个领域最常见的。
  2. 代(一)——零代二代之间的缓冲
  3. 代(三) - 保存需要持续一定时间的长寿命对象,例如静态变量和全局变量。

未被回收的对象(在Zero中)被移动到One 中,这些被称为幸存者。然后未被回收的对象(在One中)到达Two中。这是对象可以到达的最终境界。

垃圾收集器通过以下方式确定对象是否还存在:

  1. 收集由用户代码或 CLR 分配的对象的所有句柄。
  2. 跟踪静态对象,因为它们被其他对象引用。
  3. 使用堆栈遍历器JIT提供的堆栈

垃圾收集可以通过以下方式进行:

  1. 当虚拟内存空间不足时 - 自动。
  2. 当分配的内存超出可接受的阈值时,阈值分配会增加 - 自动。
  3. 当明确调用GC.Collect()方法时- 手动。

在讨论析构函数之前,我们需要了解两个关键术语。第一个概念是托管对象,第二个概念是非托管对象

当对象处于 CLR 范围内时,我们谈论的是托管对象;它是纯 .NET 代码,由运行时管理。任何来自 .NET 的东西,如类、基本数据结构(如字符串、整数等)都称为托管代码。

当对象不在 .NET 库的控制范围内并且不受 CLR 管理时,我们谈论的是非托管对象。这些对象包括 COM 对象、文件和网络流、连接对象等...

释放与非托管代码相关的资源托管代码更复杂。垃圾收集器只能跟踪这些资源,但无法释放它们。

清理非托管代码的方法:

  1. 实现IDisposable接口和Dispose方法。
  2. ‘使用’ 块

析构函数

析构函数是类内部的方法,其唯一目的是在不再需要时销毁该类的实例。析构函数由 .NET 框架的垃圾收集器隐式调用。只有当应用程序实现 GC.Collect ()方法时,程序员才能控制此过程;否则,框架会处理此问题。

简单来说,当对象/实例不再可访问时,它就会被销毁。

关键属性:

  1. 结构体不能有终结器,只有类可以。
  2. 仅允许一个终结器。
  3. 终结器不能被继承或重载。
  4. 无法调用终结器。
  5. 终结器不能采用修饰符或参数。

骨架代码示例:

      class Pluralsight
{ 
   // members and methods.

   // Destructor or Finalizer
   ~Pluralsight()
    {
        Console.WriteLine("The end is near!")
    }

}
    

您在 Finalizer 代码中提供的任何语句在时间到来时都将被垃圾收集器调用。

如果您愿意,您可以将析构函数定义为表达式体定义。

      class Pluralsight
{
	~Pluralsight() => Console.WriteLine("The end is near!")
}
    

在 .NET Framework 应用程序中,当程序退出时会调用终结器,但在 .NET Core 应用程序中并非如此。

当您创建终结器时,它实际上是一种语法糖。在底层,将调用Finalize()函数,该函数根据您的类代码动态生成,终结器的最终代码如下所示。这是您需要注意的隐式翻译。

      protected override void Finalize()
{
	try
	{
		// statements from the finalizer code come here.
	}
	finally
	{
		base.Finalize()
	}
}
    

这意味着,对于每个实例,从派生程度最高到派生程度最低,都会递归调用Finalize方法。

结论

对我来说,垃圾收集一直是个谜,当这些事情发生时,充满了诸如“如何?”,“何时?”,“为什么?”之类的问题。开发人员需要一段时间才能习惯这种内部机制,该机制通过清理不再需要的资源来保护操作系统免受应用程序本身的影响。我希望本指南值得您阅读,并且您找到了想要的内容!

以上内容来自杭州电子商务研究院推送
关注
关于我们
热门推荐
合作伙伴
免责声明:本站部分资讯来源于网络,如有侵权请及时联系客服,我们将尽快处理
Copyright © 2025-2027 ToB产业网址导航 公安备案 浙公网安备33010602013138号 浙ICP备16025413号-9
支持 反馈 关注 数据