C#性能优化基础:垃圾回收机制

news/2025/9/26 20:15:25/文章来源:https://www.cnblogs.com/shanfeng1000/p/19076925

  相信很多C#开发者都没有关注过内存问题,毕竟我们有垃圾自动回收机制,不用像C/C++那样,需要手动去释放。

  其实关于内存是自动还是手动回收释放,一直也是有争议的,像C/C++这样的开发者认为,内存这么珍贵,就应该让人去操作,怎么能让没有思维的机器去操作呢,而支持垃圾自动回收的开发者者认为,就是因为内存这么珍贵,才不能让人手动干预,机器更可靠!额,嗯,说的都对,都有道理,反正要么就是人靠谱,要么就是机器靠谱...

  好了,言归正传,在C#中,垃圾回收(Garbage Collection, GC)是一个自动内存管理机制,用于回收应用程序中不再被使用的对象的内存,因此,使用托管代码的开发人员无需编写执行内存管理任务的代码。C#中的垃圾回收是基于 .NET Framework 或 .NET Core/.NET 5+的公共语言运行时(CLR)的一部分。

  常用的垃圾分类方法有三种:引用计数法、标记删除法、分代回收法。

  引用计数法

    引用计数法,就是堆每个对象维护一个count字段,用来记录此对象被引用的次数1、当有新的引用指向时,引用计数+12、当该对象的引用减少时,引用计数 -13、如果对象的引用计数为0,该对象将被回收,空间将被释放

  一般的,引用技术法存在循环引用的问题,比如A引用B,B又引用A,那么A和B的引用计数都不为0,但是我们可以采用一些方法来解决这个问题。

  标记删除法

    标记清除是一种基于追踪回收的算法:1、第一步从跟对象出发,一直往后遍历,将那些可以被引用的对象标记为活动对象,那么剩下没有引用标记为非活动对象2、第二步就是将非活动对象删除回收了

  一般的,标记删除法存在性能问题,因为它需要扫描所有使用的内存。

  分代回收法

    分代回收法就是根据回收次数,将内存垃圾氛围3类:1、第0代(Generation 0):这是新分配的对象所在的代,由于新分配的对象很可能很快变为不可达(即不再被使用),因此第0代是垃圾回收最频繁检查的代。2、第1代(Generation 1):当第0代中的对象在一次垃圾回收后仍然存活时,它们会被提升到第1代。第1代中的对象在垃圾回收中的检查频率低于第0代,因为它们存活的时间更长,但是数量一般是最少的。3、第2代(Generation 2):类似地,如果第1代中的对象在另一次垃圾回收后仍然存活,它们会被提升到第2代。第2代是检查频率最低的代,因为其中的对象存活时间最长。

  C#的GC(Garbage Collection)

  C#的代码运行在CLR中,CRL负责资源申请、释放、异常监控等,内存属于资源的一种,所以,除非必须,C#代码不应该直接申请内存资源,而内存的释放交由GC(Garbage Collection)来控制,GC是分代回收器

  程序启动加载 CLR 时,GC 分配两个初始堆段:一个用于小型对象(小型对象堆或SOH),一个用于大型对象(大型对象堆或LOH)。

    大对象:对象的大小大于或等于 85,000 字节,运行时会将其分配到大型对象堆。

  对于大对象,它在第0代创建后,将会延续到第2代,放到LOH中,第 2 代垃圾回收未处理的对象仍是第 2 代对象,LOH 未处理的对象仍是 LOH 对象,所以LOH的回收是在第2代回收的时候进行的,第2代执行的垃圾回收被称为完整回收(Full GC),大对象堆(LOH)是我们开发者需要重点关注的

    GC回收的整个过程过程大概是这样:1、分配:申请内存,存放数据用于计算,此时数据可能在SOH或者LOH中2、晋升:根据引用打标记,应用程序中不再被使用的对象视为垃圾,按回收次数分为0、1、2代,数据在不同代中回收3、回收:清理数据,释放内存4、压缩:整理数据,退回多余的内存(LOH没有此过程)

  内存压缩

  这里解释一下内存压缩,就是GC的最后一步,我们内存是一个连续的块,但是在经历回收之后,它就变得断断续续的了,为了保证性能和资源的更好利用,GC可能会对内存做个压缩。

  对于SOH会压缩内存,这样所有的内存均可重新使用,借用官网的图说明一下:

    1、开始创建有四个对象obj0、obj1、obj2、obj3,这个时候它们在第0代2、现在obj1和obj3被释放了,那么它们的内存就空出来了,GC就会做个压缩,把obj2的位置就变到原来obj1开始的位置,同事它被提升到第1代3、如果又有obj4、obj5、obj6被提升到第1代,那么它们就会放在obj2后面,这样空间就被利用起来了4、如果obj2和obj5又被释放,那么又被压缩,obj4、obj6会向前移

  image

  而对于LOH由于压缩成本太高,因此一般是不会进行压缩(代码可以配置),在需要内存时,查看是否有可用的片段,没有则申请新的内存,但是用于申请的内存往往不一定刚好是空闲的长度,所以会流出很多Free的空间。借用官网的图说明一下:

    1、开始我们有obj0、obj1、obj2、obj3几个大对象,注意他们属于第2代2、当obj1、obj2被回收后,中间就会留下空闲片段(Free),GC一般不会去压缩3、当我们创建obj4需要新的空间时,会先看空闲片段是否满足需要,满足则使用,不满足就重新申请内存,但是哪怕满足,大概率也会有小的空闲片段

  image

  注意:GC执行垃圾回收时是阻塞操作,它会将所以线程挂起来,直至GC执行完成后继续执行。

  所以常说的GC会影响程序性能,其实就是只线程挂起来阻塞的时间,要缩短这个时间,就要尽可能少的产生垃圾,要尽可能快的执行回收,而且,如果频繁的执行GC,会发现CPU的使用率偏高,这可能对系统中的所有服务都回有一些影响。

  GC何时执行?

  GC的执行是个很复杂的过程,我们程序代码无法控制GC的执行,但是一般的,我们可以通过GC.Collect()方法来手动触发执行(虽然也不一定会执行),但是对于LOH,它往往在以下三种情况下执行:

    1、分配超出第0代或大型对象阈值2、调用 GC.Collect() 方法3、系统处于内存不足的状况

  

  总结

  在开发的过程中,为了避免GC影响性能,我们应该多注意一下。

    1、尽可能少的产生垃圾* 数据类型上,尽可能少的使用字符串、结构体之类的* 字符串使用上,注重使用字符串池(字符串暂存区),以内存的开销,对于碎片化的字符串,应采用StringBuilder优化* 注重复用,采用一些池技术优化,比如对象池2、尽可能快的执行回收,第2代回收频率是最低的,所以要尽量避免垃圾延续到第2代,特别是LOH,LOH存在的意义就是存放必须的大数据,所以非必须下不要使用LOH,LOH有着非常高的分配、回收成本。LOH中存放的数据往往是:大型集合、大字符串* 对于大型集合,比如大型的对象数据,我们需要在使用完成之后,将他们清空,以此取消他们之间的引用关系,从而可以让GC回收* 大字符串往往来自于三个地方:* 文件读取: var content = File.ReadAllText(filePath);* 格式化,如JSON格式: var json = value.ToJSON();* 数据库

 

  参考资料:

  https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/large-object-heap

 

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

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

相关文章

实验报告1

实验任务1 #include <stdio.h> #include <math.h>signed main() {//任务1// printf(" O \n");// printf("<H>\n");// printf("I I\n");//任务1_1// printf(" O …

工业网站开发商电商网站公司

ACM训练总结写这次的训练总结也就意味着这个学期快要结束了&#xff0c;“光阴似箭&#xff0c;日月如梭”&#xff0c;总觉得时间过得很快反而学的很少&#xff0c;但是这个学期待在ACM还是有收获的&#xff0c;从开学接触链表时候的一脸茫然 &#xff0c;到现在已经学完了线段…

2025.9.26——1蓝

提高+/省选- P2331 [SCOI2005] 最大子矩阵 周末的题,当时没写出来。 老师讲完状态之后发现其实不难,就是一个线性DP,只是状态和转移有点难度。

安装python解释器 - Jun

安装python解释器 1. 官方网站 下载地址 https://www.python.org/downloads/windows/ python-3.11.9-amd64.exe2. 右击管理员身份运行 .exe 自定义安装 配置path环境变量 2个 D:\Python311 D:\Python311\Scripts c…

深入解析MySQL InnoDB锁机制 - 教程

深入解析MySQL InnoDB锁机制 - 教程2025-09-26 20:07 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !imp…

根号

一. 根号分治 精髓就是拼接两个暴力 1. 余数根号分治:Remainder Problem 首先直接单点更新O(1),查询暴力跳O(n)。这个暴力的有点在于当查询的x比较大的时候,那么跳的次数就比较少。 另一种暴力思路就是维护c[i][j]为…

如何在 CentOS 7 上安装 bzip2-libs-1.0.6-13.el7.x86_64.rpm 文件

如何在 CentOS 7 上安装 bzip2-libs-1.0.6-13.el7.x86_64.rpm 文件​bzip2-libs 是 bzip2 压缩工具所需的库文件,很多软件运行时都依赖它。如果你在安装某些程序时提示缺少这个包,按本教程操作即可完成安装。 一、准…

做网站第一步区块链 做网站

开源许可证GPL、BSD、MIT、Mozilla、Apache和LGPL的区别 https://www.geek-workshop.com/thread-1860-1-1.htmlliamjeal电梯直达1# 发表于 2012-9-10 13:41:43 | 只看该作者 |只看大图 因CooCox用户数及影响力越来越大&#xff0c;CooCox团队也逐渐提高了对软件及代码协议的重…

外贸网站 球衣网站查询系统

文章目录 九九乘法表打印三角形改进:控制行数的三角形有空格的三角形 九九乘法表 package com.zhang; /* 打印九九乘法表*/ public class Test8 {public static void main(String[] args) {//i是竖着的 j是横着的for (int i 1; i < 9; i) {for(int j 1; j < 9; j) {i…

建设网站收集加工素材教案鹤壁做网站公司

使用xpath定位元素时&#xff0c;有时候担心元素位置会变&#xff0c;可以考虑使用文本内容来定位的方式。 例如图中的【股市】按钮&#xff0c;只有按钮文本没变&#xff0c;即使位置变化也可以定位到该元素。 xpath内容样例&#xff1a; # 文本内容完全匹配 //button[text(…

【A】杂题选将

P13617 [ICPC 2024 APC] Bit Counting Sequence 考虑进 1 位导致位数和减 1,那么我们找到进位最多的那一次,那么他应该是形如 111..000..0 的一个东西,然后你去判定一下就好了。

有一个[1,5]的等概率随机函数fx(),在不改变fx()函数的情况下,利用fx()函数做出一个[1,7]的等概率随机函数。

有一个[1,5]的等概率随机函数fx(),在不改变fx()函数的情况下,利用fx()函数做出一个[1,7]的等概率随机函数。题目解析首先需要将fx函数转化为一个0,1的等概率的心函数 对要转换的函数的范围分析 通过0,1等概率函数利…

WSL2 磁盘清理

首先清理 apt, snap, 各个包管理器的缓存和系统日志。 然后使用 du 或 ncdu 查看磁盘空间占用,继续清理。 等到释放了空间后,再去重置 vhdx 文件大小。 重置 vhdx 文件大小 以下路径中的用户名和包名请自行修改。 se…

洛阳市做网站的wordpress百度地图使用方法

1.2.6 嵌套表AS TABLE OF嵌套表是表中之表&#xff0c;一个嵌套表是某些行的集合&#xff0c;它在主表中表示为其中的一列。对主表中的每一条记录&#xff0c;嵌套表可以包含多个行。语法如下&#xff1a;CREATE OR REPLACE TYPE table_name AS TABLE OF type;语法说明&#x…

完整教程:1.DHCP服务器

完整教程:1.DHCP服务器pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", &qu…

关于OneBot的QQ机器人探索2

一个QQ聊天机器人,按照OneBot v11协议进行开发 目前实现的主要功能有: - echo 消息回显 - 搜索歌曲并发送音乐卡片 - AI聊天,概率性主动回复,支持在QQ中使用指令切换AI模型和提示词 - bing每日一图获取并发送 - 群…

一个网站怎样做两个后台网站建设软件培训学校

文章目录 前言1. 安装docker与docker-compose2. 启动容器运行镜像3. 本地访问测试4.安装内网穿透5. 创建公网地址6. 创建固定公网地址 前言 今天和大家分享一款在G站获得了26K的强大的开源在线协作笔记软件&#xff0c;Trilium Notes的中文版如何在Linux环境使用docker本地部署…

德阳网站设计网站 流程优化

如果经常在远程服务器或嵌入式设备中操作图片&#xff0c;要查看图片效果&#xff0c;就要先把图片dump到本地&#xff0c;比较麻烦。可以使用这个工具&#xff0c;直接在终端上显示。类似于这种效果。 imgcat 是一个终端工具&#xff0c;使用 iTerm2 内置的特性&#xff0c;允…

putty

https://www.cnblogs.com/zeoHere/p/18845009

深入解析:PHP 8.0+ 高级特性深度探索:架构设计与性能优化

深入解析:PHP 8.0+ 高级特性深度探索:架构设计与性能优化pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consol…