C#深入理解堆和栈的区别

发布时间:2023-02-11 13:30

C#深入理解栈(stack)和堆(heap)的区别

在讲栈和堆之前,我们先看看值类型和引用类型:

类别 描述
值类型 基本数据类型 枚举类型 结构类型
引用类型 类 接口 数组

1)值类型:值类型总是分配在它声明的地方,作为局部变量时,存储在栈上;作为类对象的字段时,则跟随此对象存储在堆中。

2)引用类型:引用类型存储在堆中。类型实例化的时候,会在堆中开辟一部分空间存储类的实例。类对象的引用(地址)还是存储在栈中。

    class Program
    {
        static void Main(string[] args)
        {
            byte b = 0xff;//byte类型 在栈中占 1个字节
            
            UInt16 c = 0xffff;//uint16类型 在栈中占 2个字节
            
            //person:对象的引用,存储在栈中
            //new Person():对象的实例,存储在堆中
            Person person = new Person();//引用类型

            //a:局部变量,存储在栈中 
            int a = 10;//值类型
            Console.ReadKey();
        }
    }

    public class Person
    {
        //类的字段,跟随此类存储在堆中
        public int Age { get; set; }//值类型
    }

上面写了一些很简单的代码,我们运行起来并查看了变量在栈中的地址:

&b
0x001af368
    *&b: 0xff
&c
0x001af364
    *&c: 0xffff
&person
0x001af360
    *&person: 0x02562390
&a
0x001af35c
    *&a: 0x0000000a

我们发现了几条规律:

1)先声明的变量在栈中的地址比较大,后声明的变量在栈中的地址比较小

2)在栈中,对于值类型来说,那块地址存储的就是变量的值;而对于引用类型来说,存储的是对堆的引用(在堆中的地址)
C#深入理解堆和栈的区别_第1张图片

  • 栈的结果是先进后出。与生命周期相关,越靠近栈底部的(栈地址越大的声明周期越长)

  • 栈地址是从高往低分配的,按照先后定义的顺序依次压入栈中,相邻变量的地址之间不会存在其他变量。

  • 栈是有操作系统自动分配释放的(作为程序员,你不需要显示对它做任何事),用于存储变量的值、程序执行的当前环境、方法的参数和类型的引用等。

  • 堆里的内存能够任意顺序的存入和移除,地址一般是从低往高分配。

  • 堆里的数据由CLR的自动GC在判断出程序的代码将不会再访问某项数据项时,自动清除无主的堆对象。

  • 在程序运行的时候,每一个线程都会维护一个自己的专属线程堆栈(就是我们说的栈)。当一个方法被调用的时候,主线程开始在所属程序集的元数据中,查找被调用方法,然后通过JIT即时编译并把结果(一般是本地CPU指令)放在栈顶,CPU通过总线从栈顶取指令,驱动程序以执行下去。

  • 栈通常存储着我们代码执行的步骤,而堆上存放的则多是对象、数据等。在调用方法的时候,内存从栈的顶部开始分配,保存和方法关联的一些数据项,这块内存叫做方法的栈帧(stack frame)。栈帧包含的内存保存如下内容:

    1)返回地址,也就是在方法退出的时候继续执行的位置。

    2)这些参数分配的内存,也就是方法的值参数,或者还可能时参数数组。

    3)各种和方法调用相关的其他管理数据项。

    在方法调用时,整个栈帧都会压入栈;在方法推出的时候,整个栈帧都会从栈顶弹出,弹出栈帧有的时候也叫做栈展开。

    栈内存无需我们管理,也不受GC管理。当栈顶元素使用完毕,立马释放。而堆则需要GC清理,它像一个仓库,存储着我们使用的各种对象信息,跟栈不同的是他们被调用完毕不会立即被清理掉。

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号