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

一、何为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关键词才能完成生命周期的托管 ]

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Typora快捷键设置详细教程(内附每个步骤详细截图)

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公粽号&#xff1a;程序员洲洲。 &#x1f388; 本文专栏&#xff1a;本文…

Win11系统安装安卓子系统教程

随着Win11系统的不断普及&#xff0c;以及硬件设备的更新换代&#xff0c;我相信很多同学都已经更新并使用到了最新的Win11系统。那么&#xff0c;Win11系统最受期待的功能“Windows Subsystem for Android”&#xff08;简称WSA&#xff09;&#xff0c;即《安卓子系统》。他可…

NodeJs 版本升级时Vue工程报错

最近把nodejs 的版本更新了&#xff0c;原来用的 16.15.1 现在用的 18.16.1&#xff0c;结果所有的vue工程都启不动了&#xff0c;一直报错。 1.报错截图 2.原因分析 error:03000086:digital envelope routines::initialization error &#xff0c;这个是nodejs版本的问题&am…

帆软图片设置之一张图片铺满整个屏幕

1、设置PC端自适应属性&#xff0c;字体自适应&#xff0c;双向自适应&#xff1b; 2、单元格属性–>对齐–>高级–>图片布局–>拉伸。

民爆5G智能制造工厂数字孪生可视化平台,推进民爆工业数字化转型

民爆5G工厂智能制造数字孪生可视化平台&#xff0c;推进行业数字化转型。民爆行业作为国家经济发展的重要支柱产业&#xff0c;其数字化转型对于提高生产效率、降低成本、保障安全等方面具有重要意义。而民爆5G工厂智能制造数字孪生可视化平台正是推进行业数字化转型的关键技术…

微信小程序iOS禁止上下拉显示白边

先上图暴露出问题 iOS端这个页面明明正好显示的&#xff0c;非要能下拉上拉给显示出来点白边。这样不就不好看了嘛。。 想了想是不是支持页面下拉导致的&#xff0c;加入以下代码到json文件中。 {"enablePullDownRefresh": false,"usingComponents": {} }…

JVM(4)

垃圾回收问题 垃圾回收算法 通过之前的学习我们可以将死亡对象标记出来了,标记出来后我们就可以进行垃圾回收操作了,在正式学习垃圾处理器之前,我们先来看一下垃圾回收器使用的几种算法. 标记-清除算法 "标记-清除"算法是基础的收集算法.算法分为"标记"…

【LeetCode】876_链表的中间结点_C

题目描述 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 https://leetcode.cn/problems/middle-of-the-linked-list/description/ 示例 提示&#xff1a; 链表的结点数范围是 [1, 100]1 <…

113.龙芯2k1000-pmon(12)- pmon源码对env的解析

本文回答前文思考的问题 112.龙芯2k1000-pmon&#xff08;11&#xff09;- gzrom-dtb.bin 文件的组成-CSDN博客 env写的位置是ff000 后面的500字节&#xff0c;这个位置能否改动呢&#xff1f; 答案是&#xff1a;不可以&#xff01;&#xff01;&#xff01; 否则需要改源码…

flink下载安装部署说明

下载 下载地址 flink-1.16.2下载安装包&#xff0c;flink-1.16.2-bin-scala-2.12.zip资源-CSDN文库 安装 解压目录 启动集群 ./start-cluster.sh 提交作业 ./bin/flink run examples/streaming/WordCount.jar 查看日志 停止集群 ./bin/stop-cluster.sh 开启webui vim c…

adb下载安装及使用教程

adb下载安装及使用教程 一、ADB的介绍1.ADB是什么&#xff1f;2.内容简介3.ADB常用命令1. ADB查看设备2. ADB安装软件3. ADB卸载软件4. ADB登录设备shell5. ADB从电脑上发送文件到设备6. ADB从设备上下载文件到电脑7. ADB显示帮助信息 4.为什么要用ADB 二、ADB的下载1.Windows版…

Components 概览

Components 概览 Compose 中的组件被声明为 Composable &#xff0c;中文意思是可组成的/可合成&#xff0c;也为了与原来 xml 和 View&#xff08;ViewGroup&#xff09;做出区分。在使用过程中会体会到&#xff0c;使用 Compose 声明 UI 更像是在拼图。 Compose 提供了 Mat…

2024年sCrypt编程马拉松即将开幕

BSV区块链的建设者们&#xff0c;你们在哪&#xff1f;2024年sCrypt编程马拉松即将拉开帷幕&#xff01; 2024年3月16日至17日&#xff0c;我们将在旧金山市举办一场以比特币智能合约&#xff08;即 sCrypt&#xff09;和比特币通证&#xff08;如Ordinals&#xff09;相结合为…

jstat命令查看jvm的GC信息

文章目录 前言jstat命令查看jvm的GC信息1. 概述2. 应用堆内存水位阀值大小怎么确定3. 使用 jps 命令查看 Java 进程的进程号&#xff08;PID&#xff09;![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/5097401443314e9d808a83b694dbc6e5.png)4. jstat用法5. 类加载…

农产品质量追溯系统—简介

概要 农产品质量安全事关广大人民群众的食用安全和身体健康。解决农产品质量安全问题,需要从源头开始抓好、抓实农产品安全监管工作。通过建立从产地到市场的全程质量控制系统和追溯制度,对农产品产地环境、生产过程、产品检测、包装盒标识等关键环节进行监督管理,提高广大…

php 把数字转化为大写中文

1. 120002129.25 转化后壹億贰仟萬贰仟壹佰贰拾玖圆贰角伍分2. 12000.2145 转化后壹萬贰仟圆贰角壹分肆厘伍毫3. 1020001211 转化后壹拾億贰仟萬壹仟贰佰壹拾壹圆整大致思路这样的: 从小数点分割成两部分,整数部分和小数部分分别处理。 整数四个一组进行处理,用substr函数分…

SpringBoot 事务失效及其对应解决办法

简介 本文主要讲述Spring事务会去什么情况下失效及其解决办法 Spring 通过AOP 进行事务控制&#xff0c;如果操作数据库报异常&#xff0c;则会进行回滚&#xff1b;如果没有报异常则会提交事务&#xff1b;但是&#xff0c;如果Spring 事务失效&#xff0c;会导致数据缺失/重…

几个原创文章自动生成软件推荐

在数字化时代&#xff0c;原创文章的重要性越来越被重视&#xff0c;但是频繁的写作工作却也耗费大量时间和精力。原创文章自动生成软件能够帮助大家快速生成各种类型的原创文章&#xff0c;节省时间和精力。本文将介绍6款不同的原创文章自动生成软件&#xff0c;并介绍一款知名…

mysql 常用命令练习

管理表格从表中查询数据从多个表查询修改数据sql变量类型 管理表格 创建一个包含三列的新表 CREATE TABLE products (id INT,name VARCHAR(255) NOT NULL,price INT DEFAULT 0,PRIMARY KEY(id) // 自增 ); 从数据库中删除表 DROP TABLE product; 向表中添加新列 ALTER TAB…

WPF中如何设置自定义控件

1.圆角按钮的设置&#xff1a; 众所周知在WPF中自带有提示信息&#xff0c;当我问创建Button时&#xff0c;点击空格出现如下可选设置 带有小扳手&#x1f527;图标为相应的属性&#xff0c;如果Button有CornerRadius&#xff08;角半径&#xff09;属性就能够直接设置Button实…