当前位置: 首页 > article >正文

GC的算法

在C# 中,垃圾回收器(GC, Garbage Collector)是一个自动内存管理系统。C# 的 GC 主要采用标记-清除(Mark-Sweep)和分代回收(Generational Collection)的方式来高效管理和回收不再使用的内存。下面分别介绍这两种机制。


1. 标记-清除(Mark-Sweep)

工作原理:

标记-清除是垃圾回收的核心算法之一,主要分为两个阶段:标记阶段和清除阶段。

  1. 标记阶段(Mark phase)

    • 从**根对象(Root objects)**出发(如全局变量、线程栈上的局部变量和静态变量),GC 遍历所有直接或间接引用的对象,将这些对象标记为“存活”。
    • 这个过程类似于图的遍历:GC 通过深度优先或广度优先的方式遍历对象引用图,标记所有可达的对象。
  2. 清除阶段(Sweep phase)

    • 完成标记后,所有未被标记的对象都被认为是垃圾(即不再需要的内存)。
    • GC 会遍历堆中所有对象,并清除未被标记的对象,回收这些对象所占用的内存。

优点:

  • 简单有效:算法简单,能够很好地处理对象的引用和生命周期管理。

缺点:

  • 碎片化问题:由于标记-清除只是简单地释放内存,回收的内存位置可能是不连续的,导致堆内存碎片化,从而影响后续内存分配效率。
  • 暂停时间较长:GC 必须暂停程序执行,等待标记和清除操作完成,可能导致短暂的程序“卡顿”。

2. 分代回收(Generational Collection)

工作原理:

分代回收是基于对象生命周期的观察进行优化的一种垃圾回收策略。C# 的垃圾回收器将堆分为三代,每代对象的回收策略有所不同:

  1. 第0代(Gen 0)

    • 短命对象:新分配的对象会首先进入第0代。这一代通常包含生命周期较短的对象,诸如局部变量等。
    • 快速回收:由于大多数对象很快就会变得不可达,第0代会被频繁回收。GC 在回收第0代时,只检查第0代中的对象是否存活,回收其余内存。
  2. 第1代(Gen 1)

    • 中期存活对象:如果第0代中的对象在一次GC回收后依然存活,它们会被提升到第1代。
    • 不太频繁回收:第1代的回收频率比第0代低,通常用于回收生命周期较长但最终会被释放的对象。
  3. 第2代(Gen 2)

    • 长期存活对象:如果第1代中的对象依然存活,它们会被提升到第2代。第2代主要存放生命周期较长的对象,比如全局对象、缓存等。
    • 较少回收:第2代的回收最为不频繁,因为这些对象可能长期存在。回收第2代通常是内存消耗较大时才触发的(称为完全回收)。

为什么分代回收有效:

  • 对象生命周期的特点:大部分对象的生命周期较短,因此第0代频繁回收可以快速释放大量内存。同时,由于较长生命周期的对象数量较少且不经常变化,减少对第1代和第2代的回收频率可以提升性能。

分代回收的策略:

  • 代内的局部性:GC 可以只在特定代进行回收,减少不必要的工作,避免完全扫描所有对象,从而提高效率。
  • 提升机制:当一个对象在一次回收后依然存活,GC 会认为它可能长期存活,因此将其提升到更高的一代。提升后的对象在下一代会经历较少的回收频率。

优点:

  • 高效的内存管理:分代回收利用了对象生命周期的局部性,能够在不频繁扫描整个堆的情况下,快速回收短生命周期对象,降低GC开销。
  • 减少暂停时间:因为可以针对特定代进行垃圾回收,避免对整个内存进行全面扫描,减少了程序因垃圾回收暂停的时间。

缺点:

  • 提升可能带来开销:对象从第0代到第1代、再到第2代的提升会带来一些额外的开销,尤其是当对象需要频繁提升时。
  • 第2代回收代价高:第2代对象通常是大对象,回收时可能涉及复杂的操作,并且当第2代触发回收时,会导致一次较长的暂停。

3. 标记-压缩 (Mark-Compact)

标记压缩算法是在标记清除算法的基础之上,做了优化改进的算法。
不过在 标记阶段后它将所有活动对象紧密的排在堆的一侧(压缩),消除了内存碎片,然后清理边界以外的垃圾,从而 解决了碎片化的问题
不过压缩是需要花费计算成本的。

缺点

  • 压缩过程的开销,需要多次搜索堆
  • 标记后需要定位各个活动对象的新内存地址,然后再移动对象,总共搜索了3次堆。

4. 引用计数法(Reference Count)

引用技术算法是唯一一种不用用到根集概念的GC算法。其基本思路是为每个对象加一个计数器,计数器记录的是所有指向该对象的引用数量。每次有一个新的引用指向这个对象时,计数器加一;反之,如果指向该对象的引用被置空或指向其它对象,则计数器减一。当计数器的值为0时,则自动删除这个对象。

缺点:

  • 最大的缺点,就是无法释放循环引用的对象。
  • 必须在引用发生增减时对引用计数做出正确的增减,而如果漏掉了某个增减的话,就会引发很难找到原因的内存错误。
  • 引用计数管理并不适合并行处理。

C# 的垃圾回收器通过结合这两种机制,能够在保持良好性能的同时,自动管理内存的分配与回收,极大地简化了开发者的工作。


http://www.kler.cn/news/309083.html

相关文章:

  • 从基础到高级:模块化RAG技术全览
  • 【云原生监控】Prometheus之Alertmanager报警
  • 线性基速通
  • 哪款宠物空气净化器是除浮毛王者?希喂、范罗士、霍尼韦尔宠物空气净化器实测
  • 爬坑--docker构建容器ssh连接容器环境变量会发生变化
  • Redis的IO模型
  • 计算机网络分层结构解析:OSI与TCP/IP模型
  • Blender渲染太慢怎么办?blender云渲染已开启
  • 在设计开发中,如何提高网站的用户体验?
  • Qt开发技巧(四)“tr“使用,时间类使用,Qt容器取值,类对象的删除,QPainter画家类,QString的转换,用好 QVariant类型
  • Vite项目中eslint的简单配置
  • Amazon 正式官宣取消居家上班(WFH)
  • Ubuntu apt 命令全面讲解
  • 行业机遇!程序员:如何选择适合自己的就业方向?
  • linux--防火墙
  • 【Android】处理线程中未捕获的异常
  • python加载chgcar, aeccar压缩数据
  • FRP之简单粗暴官方搭建【超详细教程】【排坑】【包括官网下载地址】【伸手党福利】
  • 容器镜像同步工具image-migrator
  • 第14章 存储器的保护
  • Linux网络子系统TCP篇 二
  • 【PostgreSQL里vacuum但是无法回收死元组的原因】
  • 解决 Docker 端口映射错误:“No public port ‘80’ published”
  • linux驱动开发-内核并发控制
  • 【网络安全】分享4个高危业务逻辑漏洞
  • 软件测试工程师面试整理-测试工具
  • unity UnityWebRequest 的request.downloadHandler 空应用
  • 承压设备032认证-全网最全解读
  • 随笔十一、wsl子系统ubuntu磁盘清理
  • 在Ubuntu 18.04上安装R的方法