.NET 之路 | 018 C# 基础:值类型和引用类型的存储结构

本文详细介绍了.NET应用程序中数据在栈和堆中的存储方式,特别是值类型和引用类型的区别。值类型存储在栈中,遵循后进先出原则,而引用类型在堆中存储实际数据,栈中存储引用。理解这些概念有助于优化内存管理和提升程序性能。
摘要由CSDN通过智能技术生成


       我们知道,程序运行时,它的数据是存储在内存中的。当我们的程序访问某个变量时,编译器负责把人们可以理解的变量名转换为处理器可以理解的内存地址,处理器通过内存地址找到内存中的存储单元,然后读取其中的数据。

  运行中的 .NET 应用程序使用两个区域来存储数据:栈和托管堆,其中托管堆简称为堆。

  我们也知道,C# 中的数据类型分为两种:值类型和引用类型。值类型包含所有的数字类型(如 byte、int、long、double 等)、布尔型(bool)、字符(char)、结构(struct)和枚举(enum),其它的都是引用类型(如类、接口、数组等)。

  数据的类型不仅决定了数据存储需要的内存大小,还决定了对象在内存中存储的位置(栈或堆)。理解值类型和引用类型的特点和它们在内存中的存储结构,就能了解它们是如何以及何时进行内存分配和回收的,这有助于帮助我们编写更高性能的应用程序。

  栈与值类型

  值类型变量的值是存储在栈中的。学过数据结构我们都知道,栈是一个后进先出(LIFO)的数据结构。这种数据结构的主要特征是,数据只能从栈的顶端插入和删除。把数据放入栈顶称为入栈,从栈顶删除数据称为出栈。用图表示如下:

  

.NET 大牛之路 | 018 C# 基础:值类型和引用类型的存储结构

  栈在内存中可以理解为上图所示的一个个连续的存储单元。栈除了存储值类型的变量,还存储传递给方法的值类型的参数,以及程序当前的执行环境等。

  我们不需要显式地对栈做任何操作,栈中数据的生命周期由 CLR 根据其作用域直接处理的。

  考虑如下代码:

  {

  int a=1; // a 的作用域开始

  // ...

  {

  int b=2; // b 的作用域开始

  // ...

  } // b 的作用域结束

  } // a 的作用域结束

  作用域的生命周期和栈的后进先出逻辑总是一致的。随着代码的执行,程序先进入变量 a 的作用域,再进入 b 的作用域。对应的,变量 a 的值先入栈,b 的值后入栈。b 的作用域先结束,它的值先出栈被销毁,其次是 a 的值出栈被销毁。

  堆与引用类型

  托管堆是一块内存区域,与栈不同的是,堆中的存储单元能能够以任意顺序存入和移除。

  对于 .NET 程序,堆中的数据是由 CLR 托管。CLR 中的 GC(垃圾回收器)判断程序将不会再访问某数据项时,会自动销毁无主的堆对象。用图表示如下:

  

.NET 大牛之路 | 018 C# 基础:值类型和引用类型的存储结构

  引用类型对象的数据存储在堆中,同时也会在栈中存储一个指向堆中实际数据的引用,用图表示如下:

  

.NET 大牛之路 | 018 C# 基础:值类型和引用类型的存储结构

  值得注意的是,对于引用类型的任何对象,其实例所有成员的数据都存放在堆中,无论它是值类型还是引用类型。

  小结

  从数据存储结构的特点来总结一下值类型与引用类型的本质区别。

  第一点不同是分配内存的时机及可变性。值类型的对象从声明开始便分配内存,声明时它在内存中占用的存储单元就固定了,销毁前不会再发生增加或减少容量,赋值只是往已分配的存储单元中写入数据;引用类型是在真正赋值或初始化时才分配内存,而且所分配的内存大小后面可能会根据需要动态发生变化(字符串类型除外)。

  第二点不同是它们的存储位置。值类型只存储在栈中,只在栈顶进行插入和删除,遵循后进先出原则;引用类型分两块存储,在堆中存储实际的数据,在栈中存储指向数据的引用。

  本文来自『.NET大牛之路』体系专栏的分享,追更完整专栏请私撩我……

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值