堆 Heap & 栈 Stack(.Net)【概念解析系列_3】【C# 基础】
〇、前言
本文主要围绕 .Net 框架中的托管堆(Heap,简称堆)和堆栈(Stack,简称栈)展开。
.Net 程序在 CLR(Common Language Runtime 公共语言运行时)上运行时,内存被从逻辑上划分为两个主要部分:堆和栈。除了栈和堆之外,CLR 还维护了其他一些内存区域,例如静态存储区域(Static Storage Area)、常量存储区域(Constant Storage Area)等。这些内存区域都有各自的特点和用途,可以帮助我们更好地管理程序内存和资源的使用。
因此,熟知堆和栈的运行机制,对提升系统性能和稳定性至关重要。
一、值类型和引用类型
在介绍主角之前我们先来了解一下值类型和引用类型。
1.1 值类型
先看下都有哪些类型属于值类型:
| 类别 | 例举 |
| 整型数值类型 |
|
| 浮点型数值类型 |
|
| 布尔类型 |
|
| Unicode UTF-16 字符 |
|
| 枚举类型 |
|
| 结构类型(structure/struct type) |
|
| 元组类型 |
|
1.2 引用类型
再来看下哪些属于引用类型:
| 类别 | 例举 |
| 内置引用类型 |
|
| 记录 |
|
| 类 |
|
| 接口 |
|
1.3 值类型与引用类型的对比
- 值类型变量声明后,不管是否已经赋值,编译器为其分配内存。
- 引用类型当声明一个类时,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其分配堆上的内存空间。当使用 new 创建一个类的实例时,分配堆上的空间,并把堆上空间的地址保存到栈上分配的小片空间中。
- 值类型的实例通常是在线程栈上分配的(静态分配),但是在某些情形下可以存储在堆中。
- 引用类型的对象总是在进程堆中分配(动态分配)。
- 值类型的变量直接包含类型的实际值。
- 引用类型包含对类型实例的引用。
- 两个不同的变量不能指向同一个值类型的值。
- 两个不同的变量可以指向同一个引用类型的值,只是这两个变量存的内存地址引用相同。
参考:https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/builtin-types/value-types
二、关于堆和栈的理解
2.1 堆和栈的概念
堆(托管堆和非托管堆)
托管堆:
其实在 C 语言中才叫堆,C# 中叫托管堆。托管堆是由 CLR(公共语言运行库 Common Language Runtime)管理,当堆中满了之后,会自动清理堆中的垃圾,因此加了“托管”两字。这也是为什么 .Net 开发不需要关心内存释放的原因。
托管堆是一块动态分配的内存区域,用于存储程序运行时需要的数据。当声明一个引用类型对象或变量时,它们被分配到堆上,并返回其引用(即指向该对象或变量在堆中存储位置的指针)。堆中的对象或变量可以通过其引用来访问和修改。
非托管堆:
.NET的程序还包含了非托管的堆,所有需要分配堆内存的非托管资源将会被分配到非托管堆上。非托管的堆需要程序员用指针手动地分配,并且手动地释放,.NET的垃圾回收和内存管理制度不适用于非托管的堆。
常见的非托管资源有:文件流、图像图形类、数据库的连接,网络连接,系统的窗口句柄,打印机资源等。

栈
栈是一种基于后进先出(Last In First Out,LIFO)原则的内存区域。栈存储几种类型的数据:某些类型变量的值、程序当前的执行环境、传递给方法的参数。当程序调用一个方法时,该方法的参数、返回地址和局部变量等数据会被压入栈中。当方法执行结束时,这些数据会从栈中弹出。
本质上讲堆栈也是一种线性结构,符合线性结构的基本特点:即每个节点有且只有一个前驱节点和一个后续节点。
栈把所有操作限制在“只能在线性结构的某一端”进行,而不能在中间插入或删除元素。把数据放入栈顶称为入栈(push), 从栈顶删除数据称为出栈(pop)。

2.2 堆和栈的关系
内存空间分配
- 堆:开发人员可以根据实际需要进行动态扩展堆的大小,但过多的使用堆,会影响程序性能和稳定性。
- 栈:栈的大小一般比较有限,不能在栈上存储过多的数据,一般用于存放函数的参数值、局部值类型变量的值、引用类型的引用等。
缓存方式
- 堆:存放在二级缓存中,生命周期由垃圾回收算法来决定,并非弃用后立马被回收,所以调用这些对象的速度要相对来得低一些。
- 栈:使用的是一级缓存, 他们通常都是被调用时从栈顶取出,用完即释放。
数据结构
- 堆:堆中一般保存的是实际的数据值,例如引用类型的实例值。
- 栈:栈中一般存储较小体量的值,例如值类型的值、引用类型的引用等。
存取排列方式
- 堆:堆里的内存能够以任意顺序存入和移除。
- 栈:LIFO 后进先出。必须从栈顶进行存取操作。
三、堆栈是如何配合来保证程序的正常运行的?
在.NET中,堆栈(stack)、托管堆(managed heap)、非托管堆(unmanaged heap)和垃圾回收机制配合使用来保证程序的正常运行。以下是它们之间的协同工作方式:
- 堆栈:堆栈用于管理函数调用和局部变量,确保函数的正确执行和返回。每个线程都有一个私有的堆栈,用于存储函数调用的上下文信息。当一个函数被调用时,相关的信息会被压入堆栈;而当函数执行结束后,这些信息会从堆栈中弹出。堆栈的快速分配和释放特性使得函数调用能够高效地进行。
- 托管堆:托管堆是.NET中用于存储托管对象的内存区域,由 CLR(Common Language Runtime)负责管理。所有的托管对象都分配在托管堆上,并由 CLR 自动进行内存管理。当需要创建一个对象时,CLR 会在托管堆上为其分配内存;而当对象不再被引用时,垃圾回收机制会定期清理无用的对象,并回收它们所占用的内存空间,防止内存泄漏和资源浪费。
- 非托管堆:非托管堆是指 .NET 应用程序使用的非托管资源的内存区域,例如通过使用平台调用接口(P/Invoke)调用的外部库或操作系统资源。非托管堆通常由操作系统或外部库分配和管理,而 .NET 应用程序通过与非托管代码进行交互来访问和操作非托管堆。
- 垃圾回收机制:垃圾回收器是 CLR 中的组件,负责管理托管堆中的内存。它会周期性地扫描托管堆,标记并清理不再被引用的对象,并释放其占用的内存空间。垃圾回收机制通过追踪对象之间的引用关系,确定哪些对象可以被安全地回收,以避免内存泄漏和资源浪费。
堆栈管理函数调用和局部变量,托管堆管理托管对象的分配和回收,非托管堆用于存储和管理非托管资源,而垃圾回收机制定期清理无用的对象,释放内存资源。这种机制确保了程序的内存使用效率和稳定性,减轻了开发人员对内存管理的负担,使他们能够更专注于应用程序的逻辑开发。
参考:https://blog.csdn.net/qq_44034384/article/details/106611384 https://qianwen.aliyun.com/chat https://blog.csdn.net/beenles/article/details/130710732
堆 Heap & 栈 Stack(.Net)【概念解析系列_3】【C# 基础】的更多相关文章
- (转)Java里的堆(heap)栈(stack)和方法区(method)(精华帖,多读读)
[color=red][/color]<一> 基础数据类型直接在栈空间分配, 方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收. 引用数据类型,需要用new来创建,既在栈 ...
- Java里的堆(heap)栈(stack)和方法区(method)
基础数据类型直接在栈空间分配, 方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收. 引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量 . 方法 ...
- .NET的堆和栈01,基本概念、值类型内存分配
当我们对.NET Framework的一些基本面了解之后,实际上,还是很有必要了解一些更底层的知识.比如.NET Framework是如何进行内存管理的,是如何垃圾回收的......这样,我们才能写出 ...
- JVM调优总结(一)-- 堆和栈的基本概念
数据类型 Java虚拟机中,数据类型可以分为两类:基本类型和引用类型.基本类型的变量保存原始值,即:他代表的值就是数值本身:而引用类型的变量保存引用值.“引用值”代表了某个对象的引用,而不是对象本身, ...
- 数据结构11: 栈(Stack)的概念和应用及C语言实现
栈,线性表的一种特殊的存储结构.与学习过的线性表的不同之处在于栈只能从表的固定一端对数据进行插入和删除操作,另一端是封死的. 图1 栈结构示意图 由于栈只有一边开口存取数据,称开口的那一端为“栈顶”, ...
- (转)堆heap和栈stack
一 英文名称 堆和栈是C/C++编程中经常遇到的两个基本概念.先看一下它们的英文表示: 堆――heap 栈――stack 二 从数据结构和系统两个层次理解 在具体的C/C++编程框架中,这两个概念并不 ...
- NET的堆和栈04,对托管和非托管资源的垃圾回收以及内存分配
在" .NET的堆和栈01,基本概念.值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配.我们知道:当执行一个方法的时 ...
- .NET的堆和栈03,引用类型对象拷贝以及内存分配
在" .NET的堆和栈01,基本概念.值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配.我们知道:当执行一个方法的时 ...
- .NET的堆和栈02,值类型和引用类型参数传递以及内存分配
在" .NET的堆和栈01,基本概念.值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配.我们知道:当执行一个方法的时 ...
- C#中的堆和栈
看到一篇讲堆和栈的文章,是我目前为止见到讲的最易懂,详细和深入的.我翻译成中文.以此总结. 原文=>C#Heap(ing) Vs Stack(ing) in .NET 在net framewor ...
随机推荐
- 微服务 - Nginx网关 · 进程机制 · 限流熔断 · 性能优化 · 动态负载 · 高可用
系列目录: 微服务 - 概念 · 应用 · 通讯 · 授权 · 跨域 · 限流 微服务 - 集群化 · 服务注册 · 健康检测 · 服务发现 · 负载均衡 微服务 - Redis缓存 · 数据结构 · ...
- 【解决方法】白嫖利用WPS自带C盘清理大师,清理C盘空间,自测清理19.5G空间,太感人了!
环境: 工具:WPS-WPS清理大师 系统版本:Windows 10 问题描述: 描述:本人C盘常年不足10G,也用过一些其他的清理工具,但是也不懂,不敢乱删除东西.一直得过且过. 由于C盘中有很多的 ...
- 2023-02-16:两种颜色的球,蓝色和红色,都按1~n编号,共计2n个, 为方便放在一个数组中,红球编号取负,篮球不变,并打乱顺序, 要求同一种颜色的球按编号升序排列,可以进行如下操作: 交换相邻
2023-02-16:两种颜色的球,蓝色和红色,都按1-n编号,共计2n个, 为方便放在一个数组中,红球编号取负,篮球不变,并打乱顺序, 要求同一种颜色的球按编号升序排列,可以进行如下操作: 交换相邻 ...
- 2022-03-22:二进制取反。 有一个二进制字符串,可以选择该串中的任意一段区间进行取反(可以进行一次或不进行),取反指将0变为1,将1变为0。那么取反之后的num可能的最大的字典序是多少呢。如有
2022-03-22:二进制取反. 有一个二进制字符串,可以选择该串中的任意一段区间进行取反(可以进行一次或不进行),取反指将0变为1,将1变为0.那么取反之后的num可能的最大的字典序是多少呢.如有 ...
- 解决:Error: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试。
启动django应用时报如下错误:Error: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试. 1.首先退出酷狗音乐再试试 2.是8000端口被其他程序占用了, ...
- PHP代码审计——ThinkPHP基础
一.ThinkPHP概述 1. ThinPHP是一个轻量级的PHP框架,旨在提供快速开发Web应用程序的工具和资源.它采用了MVC(Model-View-Controller)架构,使开发人员可以更好 ...
- Github疯传!谷歌师兄的LeetCode刷题笔记开源了!
有小伙伴私聊我说刚开始刷LeetCode的时候,感到很吃力,刷题效率很低.我以前刷题的时候也遇到这个问题,直到后来看到这个谷歌师兄总结的刷题笔记,发现LeetCode刷题都是套路呀,掌握这些套路之后, ...
- Anaconda 使用的一些体验与困惑
Channels 使用 需要注意的是做生信分析的童鞋使用 conda 环境时一定要特别注意 conda channels 的设置,滥用 channels 很有可能会导致你的软件升降级(甚至环境)错乱. ...
- JavaWeb编程面试题——MyBatis
引言 面试题==知识点,这里所记录的面试题并不针对于面试者,而是将这些面试题作为技能知识点来看待.不以刷题进大厂为目的,而是以学习为目的.这里的知识点会持续更新,目录也会随时进行调整. 关注公众号:编 ...
- Redis系列15:使用Stream实现消息队列(精讲)
Redis系列1:深刻理解高性能Redis的本质 Redis系列2:数据持久化提高可用性 Redis系列3:高可用之主从架构 Redis系列4:高可用之Sentinel(哨兵模式) Redis系列5: ...