C#的GC垃圾回收

news/2025/2/19 13:48:47/文章来源:https://www.cnblogs.com/chenxiayun/p/18717720

空间分配

在讨论垃圾回收之前,需要明白一个重要的事情,空间是怎么被分配出去的。在进程初始化时,CLR会保留一块连续的地址空间(托管堆),托管堆中维护着一个指针,称之为NextObjPtr,它指向下一个对象在堆中的分配位置。当我们在C#中调用new关键字的时候,编译器会自动生成IL指令newobj,该指令将导致CLR执行以下步骤:

  1. 计算类型(及其所有基类型)的字段,类型对象指针和同步块索引所需要的字节数。
  2. 检查是否有足够的空间储存,有的话,将对象储存在NextObjPtr指向的空间中,调用类型实例的实例构造器进行初始化,newObj指令返回对象的地址
  3. 在地址返回前,NetxObjPtr指针的值会加上对象占据的字节数,指向下一个对象占据的空间。
    image

托管还是非托管?

当进行非托管编程时,垃圾回收便成为了一个非常令我们头疼的问题。因为经常忘记释放已成为垃圾的对象空间,会造成严重的内存泄漏。幸好的是托管编程把我们解决了这个问题,通过垃圾回收,我们现在不必追踪内存的使用,也不用知道在什么时候释放内存。GC会为我们做好一切。

托管堆的优点

  1. 分配速度快,从托管堆中分配对象的速度几乎可以合从线程栈分配内存媲美
  2. 在托管堆中,连续分配的对象可以确保在内存中是连续的
  3. 托管堆会自动进行垃圾回收,当NextObjPtr加上对象需要的字节数超过地址空间的末尾,会执行一次垃圾回收。

垃圾回收算法

引用计数算法

它实际上是通过在对象头中分配一个空间来保存该对象被引用的次数。如果该对象被其它对象引用,则它的引用计数加一,如果删除对该对象的引用,那么它的引用计数就减一,当该对象的引用计数为0时,那么该对象就会被回收。
许多引用计算最大的问题就是不好处理循环引用,简单来说就是在如下代码中,2个对象的引用计数将永远是1:

    public class ObjMy{public object myRef;}public class Program{public static void Main(string[] args){var a = new ObjMy();var b = new ObjMy();a.myRef = b;b.myRef = a;a = null;b = null;}}

当出现循环引用时,2个对象永远不会删除,即使本身不在被需要。

引用跟踪算法
首先我们先明确“根”的概念:
根,每个应用程序都包含一组根。每个根都是一个储存位置,其中包含指向引用类型对象的一个指针。该指针要么引用托管
堆中的一个对象,要么为null。
可以简单认为,任何引用类型的变量都被认为是根,值类型永远不会是根。

引用跟踪算法只关心引用“类型的变量”(根),因为只有这种变量才能引用堆上的对象。

  1. CLR开始GC时,会暂停进程中所有线程。防止线程在CLR检查期间访问对象并更改其状态。
  2. CLR进入GC标记阶段,会遍历托管堆中所有对象,将同步块索引字段中的一位设为0,表明索引对象都应该删除。然后CLR检查“活动根”(不为null的根),查看它们引用了哪些对象。
  3. 任何根如果引用了堆上的对象,CLR都会标记那个对象,将同步块索引字段设为1。
  4. 一个对象被标记后,CLR才会查看对象其中的根,标记对象中的活动根,这样就避免引用计数算法的循环引用问题。
  5. 检查完毕后,已标记的对象被称为“可达的”,未标记对象被称为“不可达的”,因为应用程序中不在存在使对象能够被访问的根。

CLR所有对象都标记完成后,就进入GC的压缩阶段。

GC压缩

在GC压缩过程中,所有的幸存对象都将在内存中紧挨在一起,这有几个好处。其一,恢复了引用的“局部化”,减小了应用程序的工作集,提升了将来访问这些对象时的性能;其二,解决了原生堆的空间碎片化问题。
当然,GC压缩会使得对象在内存中地址产生变化,在压缩的最后一阶段,CLR会计算每个根的新的地址,保证每个根的引用还是和之前一样的对象。
image
如果CLR在一次GC后回收不了内存,而且进程中也没有更多的空间来分配新的GC区域,就说明该进程的内存已被耗尽了。此时,new操作符会抛出OutOfMemoryException异常。

CLR的GC是“基于代的垃圾回收器”,它对代码做出以下三种假设:

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

CLR在初始化时是不包含对象的,添加到堆中的对象被称为第0代对象。它还会为第0代对象选择一个预算(以KB为单位)。如果分配一个新对象造成第0代超过预算,就会启动一次垃圾回收。在垃圾回收后存活下来的对象会升代,成为第1代对象。
在有第1代对象的前提下,进行垃圾回收时。垃圾回收器会利用JIT编译器内部的一个机制,检查老对象是否有新数据被写入,如果有,才会对字段发生变化的老对象进行检查判断是否引用了第0代中任何新对象。
image
CLR初始化时,除了会为第0代对象选择预算,也会为第1代对象选择预算。
开始一次垃圾回收时,垃圾回收器还会检查第1代占用了多少内存。在上例中,由于第1代对象远少于预算,所以会忽略对第1代中对象的检查。
托管堆只支持三代:第0代,第1代,第2代。
CLR初始化时,除了会为第0代对象选择预算,也会为第1代对象选择预算。
开始一次垃圾回收时,垃圾回收器还会检查第1代占用了多少内存。在上例中,由于第1代对象远少于预算,所以会忽略对第1代中对象的检查。
托管堆只支持三代:第0代,第1代,第2代。

CLR的垃圾回收器的自调节:
如果垃圾回收器在回收垃圾后存活下来的对象很少,就可能减少第0代的预算。这意味着垃圾回收将更频繁,但垃圾回收器
每次做的事情也减少了,这意味着减小了进程的工作集。另一方面,如果垃圾回收器回收了第0代,还是有很多对象存活,
会增大第0代的预算。
除了第0代外,1,2代也会通过类似的启发式算法调整预算。从而提升了应用程序的整体性能。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ConcurrentHashMap(JDK1.8)put分析

一、ConcurrentHashMap整体结构 ConcurrentHashMap的数据结构与HashMap差不多,都是Node数组+红黑树+链表;ConcurrentHashMap中table的节点类型有 3 类:Node节点,是链表类型的节点;这类节点hash 大于 0 ;在扩容时ConcurrentHashMap会有一个特殊的标志对象:ForwardingNode…

如何接入sbus航模遥控器?

买了航模遥控器不会用sbus?快来快来我来教你呀(滑稽如何接入sbus航模遥控器? 最近队内大疆的遥控器短缺,故自行购买一款便宜好用的遥控器来替代,但是协议与dbus不同,研究了一阵子,所以诞生了这一篇文章来帮助大家少走弯路。 遥控器构成 一般都有发射端和接收端:发射端为…

2/15图论浅讲(知识点)

2/15图论浅讲(知识点) (后期会转移博客,所以排版不太好) 前置知识-vector 动态数组 操作1:创建一个动态数组 vector<数据类型> 数组名字操作2:插入元素 O(logn) 方式1:q.push_back(数据);在数组最后面塞数据 方式2:q.insert(q.begin()+i,a);i为下标,将a数据插入…

char**指针与const

1,char**char c = 0;char* p1 = &c; //p1是一个指针变量, 存放的是char类型变量的地址 *p1 = 1; const char* p2 = &c; //p1是一个指针变量, 存放的是char类型变量的地址, const修饰*, 表示指针内容(*p2)不可变char** p3; //p3是一个指针变量, 存放的是char*类型变量的…

升鲜宝供应链管理系统重构版发布(技术点:Java8、mysql8.0 uniapp、vue、android、web 框架: Vue3+Spring Boot3) ,界面功能t升级(四 )

升鲜宝供应链管理系统重构版发布(技术点:Java8、mysql8.0 uniapp、vue、android、web 框架: Vue3+Spring Boot3) ,界面功能t升级(四 ) 工作台 系统 商品 客户 门店 订单

(自适应手机端)英文外贸网站模板 日用百货网站源码下载

(自适应手机端)英文外贸网站模板 日用百货网站源码下载 PbootCMS内核开发的网站模板,该模板适用于外贸网站模板、日用百货网站源码等企业,当然其他行业也可以做,只需要把文字图片换成其他行业的即可; 自适应移动端,同一个后台,数据即时同步,简单适用!附带测试数据!友好…

Markdown 语法学习

Markdown 语法学习 标题: # + 三级标题 四级标题 字体 hello,world! hello,world! hello,world! hello,world! 引用选择狂神说Java>这是引用分割线图片超链接 博客园 - 开发者的网上家园) 列表A B CA B C表格名字 性别 生日张三 男 1997.1.1李斯 女 1995.7.6代码hello

一级倒立摆平衡控制系统MATLAB仿真,可显示倒立摆平衡动画,对比极点配置,线性二次型,PID,PI及PD五种算法

1.算法仿真效果 本课题是针对博主原来写的文章《基于MATLAB的一级倒立摆控制仿真,带GUI界面操作显示倒立摆动画,控制器控制输出》的升级。升级内容包括如下几个方面:增加了PI控制器,PD控制器,同时对极点配置,线性二次型,PID,PI及PD五种算法的控制输出曲线进行对比。matlab2…

使用Spring Initializr方式构建Spring Boot项目

按教材 下例选用IntelliJ IDEA 2024.3.3付费版 创建项目JDK版本根据实际情况。启动类简析 package com.example.demo; // 这段代码位于 com.example.demo 这个包下import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBo…

未注册类。WSL 安装似乎已损坏。WslRegisterDistribution failed with error: 0x8007019e/

前言 不知何时,我的wsl环境又坏了。 其实wsl我不怎么用,但是今天突然想用一下docker,但是没有启动成功。 于是乎我试了一下wsl,发现是wsl完全坏了。 之前卸载过wsl的某个子系统,比如ubuntu22,当初想着换24,之后很长一段时间没用过wsl。 尽管我怀疑是这个操作造成的问题,…

清华大学最新力作,DeepSeek 从入门到精通PDF

前几天,我发布了一篇关于 WPS 集成 DeepSeek 的教程,没想到大家的反响非常热烈!很多朋友都表示,通过这篇教程学到了不少关于 DeepSeek 的实用技巧,赶紧点进来看。不过,今天我给大家带来了更猛的干货——清华大学最新发布的 DeepSeek 资料!而且这次的资料更加关注 DeepSe…

安装程序无法自动安装virtual machine

安装VMware Tools 失败 发现在给过旧的系统安装 安装VMware Tools 的时候,会失败,比如 win7、win server2008等。原因 官网的说明,在这里 看不懂的自行翻译: 微软已将 Windows 驱动程序签名更改为自 2019 年 12 月 3 日起仅使用 SHA-2 算法,不支持 SHA-2 代码签名的旧版 W…