【C#学习笔记】内存管理

文章目录
- 分配内存
- 释放内存
- GC
- 标记清除算法
- 分代算法
- 大对象和小对象
- .NET的GC机制有这样两个问题:
官方文档
自动内存管理
自动内存管理是CLR在托管执行过程中提供的服务之一。 公共语言运行时的垃圾回收器为应用程序管理内存的分配和释放。 对开发人员而言,这就意味着在开发托管应用程序时不必编写执行内存管理任务的代码。 自动内存管理可解决常见问题,例如,忘记释放对象并导致内存泄漏,或尝试访问已释放对象的内存。
分配内存
总的来说,C#内存是分为四个区块的,分别是
- 全局数据区:存放全局变量,静态数据,常量
- 代码区:存放所有程序代码
- 栈区:存放为运行而分配的局部变量,参数,返回数据,返回地址等
- 堆区:即自由存储区
在进程中,代码执行的两大区域,是栈和堆。在栈中,代码以栈的形式依次入栈出栈执行,每个线程会维护一个属于自己的线程栈,而栈区的内存地址从上往下增加:
摘自一文读懂C#的 堆、栈、值类型、引用类型
深入理解C#中的堆(Heap)与栈(Stack),一次性全都掌握!
C#垃圾回收机制详解

相信对于代码在栈中的执行,不少计算机专业的都相当清楚。而在堆中却不是这样,初始化新进程时,运行时会为进程保留一个连续的地址空间区域。 这个保留的地址空间被称为托管堆。
托管堆是专门用于C#的自动内存管理的,它由CLR进行管理。
托管堆维护着一个指针,用它指向将在堆中分配的下一个对象的地址。最初,该指针设置为指向托管堆的基址。与栈不同,堆是从下往上的,因此所有自由空间都在已用空间的上面。而与栈不同的最大特点就是,我们往堆中存取时是自由存取的,可以以任意顺序进行存取和移除。

(乱七八糟的堆)
记得我在之前曾经讲过,C#的变量类型存在两种,第一种值类型,它是直接存储在栈上的,且使用值类型赋值,则需要完全复制一个副本存在堆栈上。而我们的操作都是在栈上实现的,因为栈上的操作速度快,内存分配和释放简单。
而引用类型则方便许多,一方面,引用类型的本体是存储在堆中的,在堆中的存取更加自由。另一方面,我们会往栈中内存入栈存储了堆地址变量。当我们想要访问引用类型时,只需通过栈中的地址就可以找到它。但是却不能直接访问堆,因为数据存储太随意,如果没有栈中的指针,我们根本不知道它在堆中的什么位置。

但是使用堆引发一个问题:在栈中,由于程序执行完毕,各种入栈的变量会自动释放。但是堆首先存取随意,如果程序员忘了释放用完的变量,就会浪费内存。其次堆中的变量就算释放了,那么被释放的变量对应内存就空在那里了。如果空着的连续内存太小我们根本用不了,就会产生内存碎片。因此C#使用托管堆,由GC机制帮助我们自动管理这个堆。
应用程序创建第一个引用类型时,将为托管堆的基址中的类型分配内存。
应用程序创建下一个对象时,垃圾回收器在紧接第一个对象后面的地址空间内为它分配内存。
只要地址空间可用,垃圾回收器就会继续以这种方式为新对象分配空间。
从托管堆中分配内存要比非托管内存分配速度快。 由于运行时通过为指针添加值来为对象分配内存,所以这几乎和从堆栈中分配内存一样快。
另外,由于连续分配的新对象在托管堆中是连续存储,所以应用程序可以快速访问这些对象。
总结:栈主要用于存储局部变量和方法调用信息,它的操作速度较快;而堆主要用于存储动态分配的对象,它的操作速度较慢但能够支持更长的生命周期和对象共享。对于数据较小、生命周期短暂的变量,可以考虑使用栈;对于较大的对象或需要长时间存活的对象,需要使用堆。
释放内存
让我们了解一下GC机制是怎么释放内存的。
.NET 的垃圾回收器管理应用程序的内存分配和释放。 每当有对象新建时,公共语言运行时都会从托管堆为对象分配内存。 只要托管堆中有地址空间,运行时就会继续为新对象分配空间。 不过,内存并不是无限的。 垃圾回收器最终必须执行垃圾回收来释放一些内存。 垃圾回收器的优化引擎会根据所执行的分配来确定执行回收的最佳时机。 执行回收时,垃圾回收器会在托管堆中检查应用程序不再使用的对象,然后执行必要的操作来回收其内存。
当满足以下条件之一时将发生垃圾回收:
-
系统具有低的物理内存。 内存大小是通过操作系统的内存不足通知或主机指示的内存不足检测出来的。
-
由托管堆上已分配的对象使用的内存超出了可接受的阈值。 随着进程的运行,此阈值会不断地进行调整。
-
调用 GC.Collect 方法。 几乎在所有情况下,你都不必调用此方法,因为垃圾回收器会持续运行。 此方法主要用于特殊情况和测试。
GC
总体来看C#的GC和Lua的GC其实差不多
引用跟踪:GC的第一步是通过引用跟踪,确定哪些对象仍然被活动的引用所引用。GC会从栈上的根对象开始,根对象包括方法中的局部变量和静态变量。然后,从根对象开始,跟踪引用链,找出所有的可达对象。
根对象标记:在引用跟踪的过程中,GC会将所有可达的对象标记为活动对象。这些活动对象将被保留下来,不会被清理。
标记清除算法
对象标记:GC接下来会遍历堆中的所有对象,从根对象可达的对象开始,将它们标记为活动对象。GC使用一种叫做“标记-清除(mark and sweep)”的算法来标记对象。首先所有的堆中的对象全部默认为垃圾,接着通过遍历栈找到引用的对象,给这些被引用的对象打上标记。最后将所有的垃圾内存释放(或者标记为空闲状态)。
压缩阶段:当清理完垃圾之后,堆中的内存会变得零零散散的,为了避免内存碎片,接着就会把所有存活的对象移动到堆的底部。

分代算法
分代算法的原理基于统计学,简单来说活动时间越长的对象使用率越高,则死亡率越低,越不需要进行清理。
分代算法的假设前提条件:
1、大量新创建的对象生命周期都比较短,而较老的对象生命周期会更长
2、对部分内存进行回收比基于全部内存的回收操作要快
3、新创建的对象之间关联程度通常较强。heap分配的对象是连续的,关联度较强有利于提高CPU cache的命中率
.NET将heap分成3个代龄区域: Gen 0、Gen 1、Gen 2

每次迭代时使用标记清除算法去除垃圾。而堆将被分为3个代龄区域,相应的GC有3种方式: # Gen 0 collections, # Gen 1 collections, #Gen 2 collections。
当Gen0区域内存达到阈值将触发# Gen 0 collections后,存活的对象将进入Gen1区域;
当Gen1区域内存达到阈值将触发 # Gen 1 collections,将Gen0存活对象放入Gen1,Gen1存活对象放入Gen2。
当Gen2区域内存达到阈值将触发 # Gen 2 collections,将Gen0存活对象放入Gen1,Gen1存活对象放入Gen2,Gen2存活对象位置不变。
Gen 0和Gen 1比较小,这两个代龄加起来总是保持在16M左右;Gen2的大小由应用程序确定,可能达到几G,因此0代和1代GC的成本非常低,2代GC称为fullGC,通常成本很高。粗略的计算0代和1代GC应当能在几毫秒到几十毫秒之间完成,Gen 2 heap比较大时fullGC可能需要花费几秒时间。大致上来讲.NET应用运行期间2代、1代和0代GC的频率应当大致为1:10:100。
和Lua一样,当对象被GC清理的时候,会调用一个终结器函数。
大对象和小对象
GC将对象分为小型和大型对象。 如果是大型对象,它的某些特性将比对象较小时显得更为重要。 例如,压缩大型对象(也就是在内存中将其复制到堆上的其他地方)的费用相当高。 因此,垃圾回收器将大型对象放置在大型对象堆 (LOH) 上。
如果对象的大小大于或等于 85,000 字节,将被视为大型对象。 此数字根据性能优化确定。 对象分配请求为 85,000 字节或更大时,运行时会将其分配到大型对象堆。大型对象属于第 2 代,因为只有在第 2 代回收期间才能回收它们。
加载 CLR 时,GC 分配两个初始堆段:一个用于小型对象(小型对象堆或 SOH),一个用于大型对象(大型对象堆LOH)。对于 SOH,GC 未处理的对象将提升为下一代。而LOH即使未被处理,它也属于第2代而不会提升为下一代。而且在大对象堆中不会进行压缩,如果回收后产生的内存碎片的大小小于85000byte,那么这个碎片就在这个程序的生命周期中永远不能被再次利用了。

.NET的GC机制有这样两个问题:
首先,GC并不是能释放所有的资源。它不能自动释放非托管资源。
第二,GC并不是实时性的,这将会造成系统性能上的瓶颈和不确定性。
GC并不是实时性的,这会造成系统性能上的瓶颈和不确定性。所以有了IDisposable接口,IDisposable接口定义了Dispose方法,这个方法用来供程序员显式调用以释放非托管资源。使用using 语句可以简化资源管理。
相关文章:
【C#学习笔记】内存管理
文章目录 分配内存释放内存GC标记清除算法分代算法大对象和小对象 .NET的GC机制有这样两个问题: 官方文档 自动内存管理 自动内存管理是CLR在托管执行过程中提供的服务之一。 公共语言运行时的垃圾回收器为应用程序管理内存的分配和释放。 对开发人员而言…...
面试之快速学习c++11- 列表初始化和 lambda匿名函数的定义
学习地址: http://c.biancheng.net/view/3730.html 8. C11列表初始化(统一了初始化方式) 我们知道,在 C98/03 中的对象初始化方法有很多种,请看下面的代码: //初始化列表 int i_arr[3] { 1, 2, 3 }; /…...
CI/CD—Docker初入门学习
1 docker 了解 1 Docker 简介 Docker 是基于 Go 语言的开源应用容器虚拟化技术。Docker的主要目标是build、ship and run any app,anywhere,即通过对应用组件的封装、分发、部署、运行等生命周期的管理,达到应用组件级别的一次封装、到处运…...
多线程的创建,复习匿名内部类,Thread的一些方法,以及lambda的变量捕捉,join用法
一、💛 Java的Thread类表示线程 1.创建类,继承Thread重写run方法 2.创建类,实现Runnable重写run方法 3.可以继承Thread重写run基于匿名内部类 4.实现Runnable重写run基于匿名内部类 5.lamdba表达式表示run方法的内容(推荐&#x…...
瑞吉外卖系统05
哈喽!大家好,我是旷世奇才李先生 文章持续更新,可以微信搜索【小奇JAVA面试】第一时间阅读,回复【资料】更有我为大家准备的福利哟,回复【项目】获取我为大家准备的项目 最近打算把我手里之前做的项目分享给大家&#…...
D455+VINS-Fusion+surfelmapping 稠密建图(三)
继续,由surfelmapping建立的点云生成octomap八叉树栅格地图 一、安装OctomapServer 建图包 安装插件 sudo apt-get install ros-melodic-octomap-ros sudo apt-get install ros-melodic-octomap-msgs sudo apt-get install ros-melodic-octomap-server sudo apt-…...
rv1109/1126 rknn 模型部署过程
rv1109/1126是瑞芯微出的嵌入式AI芯片,带有npu, 可以用于嵌入式人工智能应用。算法工程师训练出的算法要部署到芯片上,需要经过模型转换和量化,下面记录一下整个过程。 量化环境 模型量化需要安装rk的工具包: rockchip-linux/rk…...
Android平台一对一音视频通话方案对比:WebRTC VS RTMP VS RTSP
一对一音视频通话使用场景 一对一音视频通话都需要稳定、清晰和流畅,以确保良好的用户体验,常用的使用场景如下: 社交应用:社交应用是一种常见的使用场景,用户可以通过音视频通话进行面对面的交流;在线教…...
--binlog-row-event-max-size
--binlog-row-event-max-size MySQL中用于控制rows格式的Binlog,binlog以chunk的方式存储,每个chunk的大小由binlog-row-event-max-size 进行控制; 如果event比较大的时候可以调大这个值;;改值必须是256的倍数&#…...
Jmeter命令行运行实例讲解
1. 简介 使用非 GUI 模式,即命令行模式运行 JMeter 测试脚本能够大大缩减所需要的系统资 本文介绍windows下以命令行模式运行的方法。 1.1. 命令介绍 jmeter -n -t <testplan filename> -l <listener filename> 示例: jmeter -n -t test…...
pl/sql函数如何返回多行数据?在线等......
编辑csm8109022010-01-27 09:59:18 这个问题我以前问过类似的,但一直没得到如意的答案!在oracle 里soctt的用户下的emp表,比如写一个函数,传入的参数为部门编号,然后返回所有该部门人员信息的函数。要用到游标&…...
Ubuntu Find命令详解
一、Find命令简介 Ubuntu的Find命令是一种常用的终端指令,用于在文件系统中查找符合条件的文件和目录。该命令的语法格式如下: find [PATH] [OPTION] [EXPRESSION]其中,PATH表示待查找的目录,OPTION为选项参数,EXPRES…...
ADS Momentum学习笔记
ADS Momentum的简介 ADS Layout界面仿真采用的方法主要是Momentum(矩量法)。 Momentum的特点 Momentum是高级设计系统(ADS)的重要组成部分,它提供了设计现代通信系统的电磁仿真。它可以用来计算一般平面电路的S参数…...
解决Vue3 使用Element-Plus导航刷新active高亮消失
解决Vue3 使用Element-Plus导航刷新后active高亮消失的问题 启用路由模式会在激活导航时以 index 作为 path 进行路由跳转 使用 default-active 来设置加载时的激活项。 接下来打印一下选中项index和index路径, 刷新也是没有任何问题的,active不会消失…...
K8S系列文章之 一键部署K8S环境
部署的原理是基于自动化部署工具 Ansible 实现的,需要提前安装Ansible 并配置下主机节点环境 1. 安装 Ansible 首先ansible基于python2.X 环境,默认centos都已经安装好了python2环境 // 最好更新下库 // yum update yum install -y epel-release yum i…...
Spring Boot、Spring Cloud、Spring Alibaba 版本对照关系及稳定兼容版本
Spring Boot、Spring Cloud、Spring Alibaba 版本对照关系及稳定兼容版本 引言 在 Java 生态系统中,Spring Boot、Spring Cloud 和 Spring Alibaba 是非常流行的框架,它们提供了丰富的功能和优雅的解决方案。然而,随着不断的发展和更新&…...
虫情监测仪介绍—技术原理、功能优势是什么?
KH-CQPest虫情监测仪是做好虫情监测的重要设备,利用虫情监测仪能够对农业大田、智慧温室、林业等场景的害虫分布情况及害虫种类进行监测,协助人们制定合理的防治措施。 1.技术原理: KH-CQPest虫情监测仪采用光学诱虫原理,配合传感…...
HTML5 Canvas和Svg:哪个简单且好用?
HTML5 Canvas 和 SVG 都是基于标准的 HTML5 技术,可用于创建令人惊叹的图形和视觉体验。 首先,让我们花几句话介绍HTML5 Canvas和SVG。 什么是Canvas? Canvas(通过 标签使用)是一个 HTML 元素,用于在用户计算机屏幕…...
ChatGPT在社交媒体聊天和评论分析中的应用如何?
ChatGPT在社交媒体聊天和评论分析中具有广泛的应用前景,可以帮助企业、个人和社会从多个角度更好地理解用户观点、趋势和情感。以下是详细的讨论: **1. 舆情分析与趋势预测:** ChatGPT可以用于分析社交媒体上的评论、帖子和消息,…...
DoIP学习笔记系列:(四)用CAPL脚本读取DID的关键点
文章目录 1. 如何在CAPL中读取DID?1.1 避坑如何新建CAPL工程,在此不再赘述,本章主要分享一下如何在CAPL中调用DoIP接口、diag接口进行DoIP和诊断的测试。 1. 如何在CAPL中读取DID? 通常在实际项目中,会有很多DID,各种版本号、各种观测量,如果手动点,显然很麻烦,如果要…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
