C#.NET CLR垃圾回收机制


C#.NET CLR垃圾回收机制

前言

今天我们来共同学习一下CLR的垃圾回收机制,这对我们写出健壮性的代码很有帮助,也许有人会认为多此一举,认为垃圾回收交给CLR就行,我不用关心这个,诚然,大多数情况下是这样的,但是,我们今天讨论的是程序的健壮性以及能够快速定位那些神出鬼没的问题。

一个例子

 static void Main(string[] args)
        {
            Timer timer = new Timer(OnTimer,null,0,1000);
            Console.ReadLine();
        }

        private static void OnTimer(object state)
        {
            Console.WriteLine(1);
        }

看一下上面的代码,大家认为在release模式下,会打印出来几个1?

可能会有两种答案:

  • 无限多个,1s一个
  • 不确定几个

再看下列代码:

 static void Main(string[] args)
        {
            Timer timer = new Timer(OnTimer,null,0,1000);
            Console.ReadLine();
        }

        private static void OnTimer(object state)
        {
            Console.WriteLine(1);
            GC.Collect();
        }

这次能打印出来几个1呢?是不是还是两种答案呢?

这里我先说明一个问题,开始时我已经说过了,程序时在release下运行的,为什么我们要给出这个条件呢?因为,在debug模式下,编译器会延长局部变量的生命周期直至方法的结束,而release模式下,方法中的代码下没有再调用的变量生命周期都已结束,被认为可以回收的对象,明确这一点是十分重要的。

根据上面的阐述,你是不是已经认识到:第一个代码片段的答案是【不确定几个】,因为如果我们程序实例化了很多变量,导致进行了一次垃圾回收的工作,那么变量timer就会被释放掉;而第二个代码片段,是我写出的垃圾回收的极端情况,它的答案应该是:只打印出一个1.

是不是感觉有点惊讶?!接下来,我们将共同解开CLR垃圾回收机制的神秘面纱

垃圾回收的算法比较

对于所有的托管系统来说,垃圾回收机制的算法一般包含两种:

  • 引用计数器算法
  • 引用追踪算法
    我们先来讨论【引用计算器算法】的优缺点。该算法是在每个对象的实例都有一个内存空间来存储当前被多少对象引用,引用增加是就加1,超出变量作用域的就减一直至为0,就认为该对象可以被回收了,此种算法简单有效,但它不能解决循环引用的情况,如果a引用了b,b再引用了a(a,b为两个对象的实例),那么a和b永远不会被释放.

[引用追踪算法]它只关心堆上的对象是否有变量引用它,如果没有就认为是可以回收的对象。而CLR就是使用的这种垃圾回收算法,接下来,我们来共同学习一下这种算法在CLR中的应用

垃圾回收机制的步骤

一次垃圾回收一般分为三个步骤:

  • 标记
  • 回收
  • 压缩

标记

这一步的只要工作是找到堆上没有被变量引用的对象实例。引用对象在分配内存时都加了一个区块叫【同步块索引】,该索引占64位,8个字节(64位系统上),对堆上的对象进行标记时就是用了这一块区域的某一位。

  • 在开始标记之前,先把堆上的所有对象的这一位标记为0。
  • 堆上的对象有变量指向的,这一位改成1。这表示该对象时可达的
  • 标记工作结束后,对象的【同步块索引】那一位标记为0的,就代表时可以回收的对象

标记工作的模式

标记对象的工作有两种模式:

  • 同步 :标记工作开始之处,就暂停所有线程,开始标记工作
  • 并发 :起一个低优先级的线程执行标记工作,直到找到有为0的对象,再暂停所有线程,进行垃圾回收工作

回收

回收工作就很简单了,在堆上删除掉标记为0 的对象

压缩

对象被删除后,会导致内存空间有碎片,这个时候CLR就会执行一次压缩工作,将不连续的内存使用,变成连续的;压缩后,变量的引用地址和堆上对象分配的空间地址不对应了,为了解决这个问题,CLR又执行了一次引用地址的偏移修改。之后再启动所有被暂停的线程,一次垃圾回收就执行完毕了!

垃圾回收机制的优化

上一节讲的垃圾回收机制有一个大的性能问题,它每次执行标记工作时都要扫描一遍堆上的所有对象,这是就产生了一个性能问题,微软为了解决这个问题,提出了代的概念,首先他给出了一下假设:

  • 对象越新,生存期越短
  • 对象越老,生存期越长
  • 回收堆的一部分,速度快于回收整个堆

三世同堂

CLR只支持最多3代的对象。0代、1代、2代
在CLR初始化时,CLR会对这三代回收对象各自预留一个空间,当每个代中的对象超出整个空间时,就会执行一次垃圾回收。CLR会根据程序执行情况动态的调整这三个预留空间的大小,这里我们不去了解这种动态调整的情况,接下来我们来说一下怎么产生的0、1、2代对象以及它们怎么被回收的

垃圾回收基于代的优化

  1. CLR初始化后,只有0代的对象
  2. 随着应用程序的使用,堆上0代对象的内存空间超出了CLR为其预留的空间,就会进行一次垃圾回收
  3. 本次垃圾回收,留存下来的对象,会变成1代对象
  4. 循环执行2,3步骤,当1代对象达到预留空间时,CLR会进行1代和0代对象的垃圾回收
  5. 本次垃圾回收留存下来的1代对象,变成2代对象
  6. 循环执行2,3,4,5,当2代对象达到预留空间时,CLR会进行三代对象的垃圾回收

垃圾回收的其他知识点

  • 应用程序可以强制对所有代的对象进行垃圾,需要使用 GC.Collect();Collect方法有5个重载
  • 针对大对象(85000字节以上),CLR单独在对上分配一块内存区域,其对象总是2代对象,因此,我们应该确保大对象的生命周期应该很长,否则CLR频繁对2代对象进行回收,会降低性能
  • ~ClassName(),析构函数总是在垃圾回收后执行,因此存在析构函数的对象总会被留存到下一代进行垃圾回收

CSCODE.NET开发框架文库 - C/S架构winform开发框架

CSCODE.NET开发框架文库 - C/S架构Winform开发框架

版权声明:本文为开发框架文库发布内容,转载请附上原文出处连接
C/S框架网
上一篇:在ASP.NET Core web API中使用Swagger/OpenAPI(Swashbuckle)
下一篇:C#异步编程(多线程)
评论列表

发表评论

评论内容
昵称:
关联文章

C#.NET CLR垃圾回收机制
C#.NET GC.Collect垃圾回收机制详解
C#.NET 消息机制
C#/.NET 基础学习
C#与.NET概述 - C#程序设计好文!
C/S开发框架事务处理机制
CSFramework.WebApi系统安全保障机制
WebApi接口安全机制:API接口限流防止恶意访问 ThrottlingHandler消息处理机制
C/S开发框架系统异常处理机制(Exception Handler)
ASP.NET IIS程序池被回收导致网站打开慢,IIS配置启用预加载模式
CSFramework.WebApi服务端处理流程与机制
学习C#.NET基础知识(学习重点请下载附件)
C# .NET 入门概念与知识点总结
C#与.NET之间的关系
C#/.NET Core简单认识
C# OOP编程 模拟做早餐探索同步异步机制
AspNet WebAPI后端框架消息处理机制(配置属性HttpConfiguration.MessageHandlers)
WebApi接口安全之用户认证防篡改数字签名(Data Sign)机制
CSFramework.WebApi后端框架Token令牌工作机制以及Token刷新原理
【原创】WebApi开发框架:Token生成、Token缓存原理、Token验证、令牌机制与原理

热门标签
.NET5 .NET6 .NET7 APP Auth-软件授权注册系统 Axios B/S B/S开发框架 Bug Bug记录 C#加密解密 C#源码 C/S CHATGPT CMS系统 CodeGenerator CSFramework.DB CSFramework.EF CSFrameworkV1学习版 CSFrameworkV2标准版 CSFrameworkV3高级版 CSFrameworkV4企业版 CSFrameworkV5旗舰版 CSFrameworkV6.0 DAL数据访问层 Database datalock DbFramework Demo教学 Demo下载 DevExpress教程 DOM EF框架 Element-UI EntityFramework ERP ES6 Excel FastReport GIT HR IDatabase IIS JavaScript LINQ MES MiniFramework MIS NavBarControl Node.JS NPM OMS ORM PaaS POS Promise API Redis SAP SEO SQL SQLConnector TMS系统 Token令牌 VS2022 VSCode VUE WCF WebApi WebApi NETCore WebApi框架 WEB开发框架 Windows服务 Winform 开发框架 Winform 开发平台 WinFramework Workflow工作流 Workflow流程引擎 版本区别 报表 踩坑日记 操作手册 代码生成器 迭代开发记录 基础资料窗体 架构设计 角色权限 开发sce 开发技巧 开发教程 开发框架 开发平台 开发指南 客户案例 快速搭站系统 快速开发平台 秘钥 密钥 权限设计 软件报价 软件测试报告 软件简介 软件开发框架 软件开发平台 软件开发文档 软件体系架构 软件下载 软著证书 三层架构 设计模式 生成代码 实用小技巧 收钱音箱 数据锁 数据同步 微信小程序 未解决问题 文档下载 喜鹊ERP 喜鹊软件 系统对接 详细设计说明书 行政区域数据库 需求分析 疑难杂症 蝇量级框架 蝇量框架 用户管理 用户开发手册 用户控件 在线支付 纸箱ERP 智能语音收款机 自定义窗体 自定义组件 自动升级程序