C#学习(十四)——垃圾回收、析构与IDisposable

news2025/1/20 3:49:41

一、何为GC

数据是存储在内存中的,而内存又分为Stack栈内存和Heap堆内存

Stack栈内存Heap堆内存
速度快、效率高结构复杂
类型、大小有限制对象
只能保存简单的数据引用数据类型
基础数据类型、值类型-

举个例子

var c= new Customer{
	id: 123,
	name: "Jack"
	address: "珠海"
}

在堆内存中就保存了信息

#1000
123Jack珠海

而在栈内存中仅保存了需要调用的地址c* = 1000——reference
当删除时,需要先删除堆内存的数据,再删除栈内存的数据,然而如果先删除了栈内存的数据,那么对内存中的数据就再也无法找到,也无法删除,无法重复利用,就会造成内存泄漏。
因此,为了便捷,如JAVA、C#等语言引入了垃圾回收机制,使得程序员只需要关注于对象本身即可。

如果一段对象的引用数量为0,则代表对象的声明周期结束。

二、GC是如何工作的

运行垃圾回收的成本很高,需要不断地遍历所有数据,因此使用了复杂的机制来解决高效运行问题。——Generations分代回收
将数据对象分成三组

G0G1G2
暂时性的对象中长期对象长期对象
每次运行GC都检查检查频率下降GC偶尔来检查

内存不足时,GC会强行清理所有对象

GC不止处理垃圾清理

  • [ 标记、清理堆内存中的死掉的对象]
  • [ 压缩内存、消除间隙,提高对象的创建、读取效率 ]
    不过GC不会处理过大的内存区块

GC独立线程

  • [ GC跑在独立的后台线程中 ]
  • [ 每次运行都需要付出代价,需要消耗计算资源 ]
  • [ 尽可能的减少运行频率、并且尽可能提高运行效率 ]

三、析构方法and终结器

终结器(以前称为析构函数)用于在垃圾回收器收集类实例时执行任何必要的最终清理操作。在大多数情况下,通过使用System.Runtime.InteropServices.SafeHandle或派生类包装任何非托管句柄,可以免去编写终结器的过程。

若无必要,不要使用
使用终结器会造成性能的损失。

代码举例

public class AnywayClass
{
    public AnywayClass()
    {
        Console.WriteLine("AnywayClass类创建");
    }

    ~AnywayClass()
    {
        Console.WriteLine("AnywayClass类销毁");
    }
}
class Program
{
    static void Main(string[] args)
    {
        var anyway = new AnywayClass();

        Console.WriteLine("程序结束");

    }
}

但是运行后会发现,程序并不会输出“AnywayClass类销毁”,要判断当前实例是否还会被引用,是根据语句的区域决定的,也就是说,它的作用域是整个main方法,因此垃圾回收是在整个main方法外面,因此看不到析构方法的输出。

因此要看到输出,就要降低对象的作用域。

public class AnywayClass
{
    public AnywayClass()
    {
        Console.WriteLine("AnywayClass类创建");
    }

    ~AnywayClass()
    {
        Console.WriteLine("AnywayClass类销毁");
    }
}

public class SecondClass : AnywayClass
{
    public SecondClass()
    {
        Console.WriteLine("SecondClass创建");
    }
    ~SecondClass()
    {
        Console.WriteLine("SecondClass销毁");
    }
}

public class ThirdClass : SecondClass
{
    public ThirdClass()
    {
        Console.WriteLine("ThirdClass创建");
    }
    ~ThirdClass()
    {
        Console.WriteLine("ThirdClass销毁");
    }
}
class Program
{
    static void DoSomething()
    {
        new ThirdClass();
    }

    static void Main(string[] args)
    {
        DoSomething();
        GC.Collect();//进行垃圾回收
        GC.WaitForPendingFinalizers();//等待所有需要被回收的对象全部被回收
        

        Console.WriteLine("程序结束");

    }
}

  • [ 一个类只能有一个终结器 ]
  • [ 不能继承或重载终结器 ]
  • [ 不能手动调用终结器,只能由垃圾回收器自动调用 ]
  • [ 终结器不使用修饰符或参数 ]

四、Disposable模式

GC不是万能的,GC只能处理托管资源(即那些使用new关键字创建的对象),而无法处理外部资源(比如文件的读取、数据库请求、网络访问等)。
文件读取、网络访问、数据库请求无法托管在.Net平台内部,如果不清理外部资源,将会极大的占用电脑资源,内存不断增长,最后崩溃退出。
因此使用IDisposable实现资源的释放

namespace System
{
	//释放外部资源
	public interface IDisposable
	{
		void Disposable();
	}
}

典型案例

public class Custom : Disposable
{
	void Method();
	void Dispose();
}

static main()
{
	using (var obj = new Customs())
	{
		obj.Method();
	}
}

使用Dispose方法就必须使用using关键字

五、使用IDisposable回收非托管资源

首先在nuget工具中下载安装SqlClient
SqlClient
示例代码
Program.cs

class Program
{
    static void Main(string[] args)
    {
        for(int i = 0; i < 1000; i++)
        {
            var db = new DatabaseHelper();
            var date = db.GetData();
            db.Close();
            Console.WriteLine($"[{DateTime.Now.ToLongTimeString()}; {date}]");
        }

        Console.WriteLine("程序结束");
    }
}

DatabaseHelper.cs

public class DatabaseHelper
{
    private SqlConnection _connection;

    private string _connectionString = $"数据库连接字符串;" +
        $"App = Recycle;" +
        $"Max Pool Size = 100;" +
        $"Pooling = true;";

    public string GetData()
    {
        if(_connection == null)
        {
            _connection = new SqlConnection(_connectionString);
            _connection.Open();
            Console.WriteLine("数据库连接已开启");
        }

        var command = _connection.CreateCommand();
        command.CommandText = "select getdate();";
        return command.ExecuteScalar().ToString();//完成最后输出
    }

    public void Close()
    {
        Console.WriteLine("数据库连接已关闭");
        _connection.Close();
        _connection.Dispose();//注销数据库的连接对象
        _connection = null;
    }
}

实际上,程序员忘记关闭数据库外部资源是一个十分常见的低级错误,为了避免此类错误,可以使用IDisposable接口
Program.cs

class Program
{
    static void Main(string[] args)
    {
        for(int i = 0; i < 1000; i++)
        {
            using (var db = new DatabaseHelper())
            {
                var date = db.GetData();
                Console.WriteLine($"[{DateTime.Now.ToLongTimeString()}; {date}]");
            };                
        }

        Console.WriteLine("程序结束");
    }
}

DatabaseHelper.cs

public class DatabaseHelper : IDisposable
{
    private SqlConnection _connection;

    private string _connectionString = $"Data Source=localhost\\SQLEXPRESS;Initial Catalog=master;Integrated Security=True;Encrypt=True;Trust Server Certificate=True;" +
        $"App = Recycle;" +
        $"Max Pool Size = 100;" +
        $"Pooling = true;";

    public string GetData()
    {
        if(_connection == null)
        {
            _connection = new SqlConnection(_connectionString);
            _connection.Open();
            Console.WriteLine("数据库连接已开启");
        }

        var command = _connection.CreateCommand();
        command.CommandText = "select getdate();";
        return command.ExecuteScalar().ToString();//完成最后输出
    }


    private bool disposedValue;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                // TODO: 释放托管状态(托管对象)
                Console.WriteLine("数据库连接已关闭");
                _connection.Close();
                _connection.Dispose();//注销数据库的连接对象
                _connection = null;
            }

            // TODO: 释放未托管的资源(未托管的对象)并重写终结器
            // TODO: 将大型字段设置为 null
            disposedValue = true;
        }
    }

    // // TODO: 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器
    // ~DatabaseHelper()
    // {
    //     // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
    //     Dispose(disposing: false);
    // }

    public void Dispose()
    {
        // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }
}

注:

  • [ Dispose可以用来回收如数据库连接、文件读取、HTTP长连接等无法托管在.net平台中的外部资源 ]
  • [ 使用Dispose必须实现IDisposable接口 ]
  • [ IDisposable接口需要配合using关键词才能完成生命周期的托管 ]

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

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

相关文章

自定义神经网络二之模型训练推理

文章目录 前言模型概念模型是什么&#xff1f;模型参数有哪些神经网络参数案例 为什么要生成模型模型的大小什么是大模型 模型的训练和推理模型训练训练概念训练过程训练过程中的一些概念 模型推理推理概念推理过程 总结 前言 自定义神经网络一之Tensor和神经网络 通过上一篇…

[深度学习]yolov9+deepsort+pyqt5实现目标追踪

【YOLOv9DeepSORTPyQt5追踪介绍】 随着人工智能技术的飞速发展&#xff0c;目标追踪在视频监控、自动驾驶等领域的应用日益广泛。其中&#xff0c;YOLOv9作为先进的目标检测算法&#xff0c;结合DeepSORT多目标追踪算法和PyQt5图形界面库&#xff0c;能够为用户提供高效、直观…

深度学习500问——Chapter01:数学基础

文章目录 前言 1.1 向量和矩阵 1.1.1 标量、向量、矩阵、张量之间的联系 1.1.2 张量与矩阵的区别 1.1.3 矩阵和向量相乘结果 1.1.4 向量和矩阵的范数归纳 1.1.5 如何判断一个矩阵为正定 1.2 导数和偏导数 1.2.1 导数偏导计算 1.2.2 导数和偏导数有什么区别 1.3 特征值和特征向量…

文献阅读:Transformers are Multi-State RNNs

文献阅读&#xff1a;Transformers are Multi-State RNNs 1. 内容简介2. 方法介绍 1. 基础回顾 1. RNN2. Transformer 2. Transformer解构 1. MSRNN2. Transformer 3. TOVA 1. 现有转换策略2. TOVA 3. 实验考察 & 结论 1. 实验设计2. 实验结果 1. LM2. 长文本理解3. 文本生…

(十三)【Jmeter】线程(Threads(Users))之tearDown 线程组

简述 操作路径如下: 作用:在正式测试结束后执行清理操作,如关闭连接、释放资源等。配置:设置清理操作的采样器、执行顺序等参数。使用场景:确保在测试结束后应用程序恢复到正常状态,避免资源泄漏或对其他测试的影响。优点:提供清理操作,确保测试环境的整洁和可重复性…

通天星CMSV6 车载视频监控平台信息泄露漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

元数据思想-打破传统的思维方式

本文已收录公众号(面汤放盐)&#xff1a;元数据思想-打破传统的思维方式 本文是一篇讲解元数据案例的技术文章; 同时也谈论如何对传统 CRUD 进行破局的文章。 元数据思想-打破传统的思维方式 打破传统的思维模式&#xff0c; 跳出固有的认知模型&#xff0c;从更高的视角去理…

社区分享|中华保险基于MeterSphere开展接口自动化测试

中华联合保险集团股份有限公司&#xff08;以下简称为“中华保险”&#xff09;始创于1986年&#xff0c;是全国唯一一家以“中华”冠名的国有控股保险公司。截至2022年12月底&#xff0c;中华保险总资产为1006.06亿元&#xff0c;在全国拥有超过2900个营业网点&#xff0c;员工…

Python入门必学:reverse()和reversed()的区别

Python入门必学&#xff1a;reverse()和reversed()的区别 &#x1f4c5;2024年02月25日 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程…

ABAP - Function ALV 05 添加选择框列、全选、取消全选

很多用户不习惯原生GRID的选择模式&#xff0c;所以业务需要用到自定义的选择框来进行数据的操作&#xff0c;显示效果如图所示&#xff0c;增加一条选择列&#xff0c;且配置全选和全选全选的按钮功能&#xff0c;如下图所示。 实现这种功能需要用到Fieldcat的参数控制以及GUI…

[02 git ] 清华大学电子系科协软件部2023暑期培训

本视频为清华大学电子系科协软件部2023年暑期培训内容的录屏&#xff0c;主要培训内容为游戏开发、网站建设中常用的软件工具&#xff0c;为未来一年软件部新部员维护科协网站、开发清华大学人工智能挑战赛&#xff08;THUAI&#xff09;作知识铺垫。本次培训还邀请到两位嘉宾讲…

Apache celeborn 安装及使用教程

1.下载安装包 https://celeborn.apache.org/download/ 测0.4.0时出现https://github.com/apache/incubator-celeborn/issues/835 2.解压 tar -xzvf apache-celeborn-0.3.2-incubating-bin.tgz 3.修改配置文件 cp celeborn-env.sh.template celeborn-env.shcp log4j2.xml.…

【PX4SimulinkGazebo联合仿真】在Simulink中使用ROS2控制无人机进入Offboard模式起飞悬停并在Gazebo中可视化

在Simulink中使用ROS2控制无人机进入Offboard模式起飞悬停并在Gazebo中可视化 系统架构Matlab官方例程Control a Simulated UAV Using ROS 2 and PX4 Bridge运行所需的环境配置PX4&Simulink&Gazebo联合仿真实现方法建立Simulink模型并完成基本配置整体框架各子系统实现…

人工智能绘画的时代下到底是谁在主导,是人类的想象力,还是AI的创造力?

#ai作画 目录 一.AI绘画的概念 1. 数据集准备&#xff1a; 2. 模型训练&#xff1a; 3. 生成绘画&#xff1a; 二.AI绘画的应用领域 三.AI绘画的发展 四.AI绘画背后的技术剖析 1.AI绘画的底层原理 2.主流模型的发展趋势 2.1VAE — 伊始之门 2.2GAN 2.2.1GAN相较于…

香港服务器掉包原因及处理方法

在使用香港服务器的过程中&#xff0c;有时会遇到“掉包”现象&#xff0c;即数据传输过程中数据包丢失或延迟。这不仅影响用户体验&#xff0c;还可能对企业运营造成不良影响。那么&#xff0c;香港服务器掉包的原因是什么?又该如何处理呢?小库评测将为您科普相关知识。 一、…

消息中间件篇之RabbitMQ-消息重复消费

一、导致重复消费的情况 1. 网络抖动。 2. 消费者挂了。 消费者消费消息后&#xff0c;当确认消息还没有发送到MQ时&#xff0c;就发生网络抖动或者消费者宕机。那当消费者恢复后&#xff0c;由于MQ没有收到消息&#xff0c;而且消费者有重试机制&#xff0c;消费者就会再一次消…

一招鲜吃遍天!ChatGPT高级咒语揭秘:记忆、洗稿、速写SEO文章(一)

🌟 摘要 🌟 这个专栏系列的初衷是针对特定痛点精心设计GPT提示词,在这篇文章中,我们深入探讨了利用GPT技术解决三个常见挑战:增强记忆力、内容创新、以及SEO文章速写的高级技巧。这些挑战分别对应三个独特的解决策略,我们将逐一详细解析。 首先,解决记忆增强的挑战,我…

聊聊JVM运行时数据区的堆内存

聊聊JVM运行时数据区的堆内存 内存模型变迁&#xff1a; Java堆在JVM启动时创建内存区域去实现对象、数组与运行时常量的内存分配&#xff0c;它是虚拟机管理最大的&#xff0c;也是垃圾回收的主要内存区域 。 内存模型变迁&#xff1a; 为什么要有年轻区和老年区&#xff1f;…

js逆向-2

#md5加密&#xff0c;某宝案例演示。 #免责声明:本文仅供学习使用&#xff0c;请勿用于其他违法行为(╥ω╥)

通过STM32F756 QSPI来读写数据

通过STM32F756 QSPI来读写数据 手上做了一款STM32F756IGT6的开发板&#xff0c;是网上的公板子&#xff0c;扩展了32MB SDRAM&#xff0c; 但QSPI有一个引脚是有错误&#xff0c; 后面找了出来&#xff0c; 同时引出了大量的IO接口&#xff0c;可以支持LCD&#xff0c;但我没有…