C#的栈与堆

2019年2月21日 (Location:ordos)

  年前在面试Lenovo的时候,面试官问了关于堆和栈的相关问题,一直没有搞清楚栈和堆,此文为解决该困惑。

一、堆和栈的叫法

:在c里面叫堆,在c#里面其实叫托管堆。

:就是堆栈,因为和堆一起叫着别扭,就简称栈了。

托管堆:托管堆不同于堆,它是由CLR(公共语言运行库(Common Language Runtime))管理,当堆中满了之后,会自动清理堆中的垃圾。所以,做为.net开发,我们不需要关心内存释放的问题。

内存堆栈和数据结构堆栈

①数据结构堆栈:是一种后进先出的数据结构,它是一个概念,栈是一种后进先出的数据结构。

②内存堆栈:存在内存中的两个存储区(堆区,栈区)。

栈区:存放函数的参数、局部变量、返回数据等值,由编译器自动释放

堆区:存放着引用类型的对象,由CLR释放

二、值类型和引用类型

1、值类型和引用类型的内存分配。

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

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

详细陈述:
1、值类型变量声明后,不管是否已经赋值,编译器为其分配内存。
2、引用类型当声明一个类时,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其分配堆上的内存空间。当使用 new 创建一个类的实例时,分配堆上的空间,并把堆上空间的地址保存到栈上分配的小片空间中。
3、值类型的实例通常是在线程栈上分配的(静态分配),但是在某些情形下可以存储在堆中。
4、引用类型的对象总是在进程堆中分配(动态分配)。
5、值类型在栈内分配空间大小因变量类型而异。
6、引用类型在栈内的空间大小相同;(堆上地址)。
7、数组的元素不管是引用类型还是值类型,都存储在托管堆上。
8、对象在c#中默认的是用过引用传递的:其实在调用方法的时候,参数值(对象的一个引用)是以传值得方式传递的,如果你想以引用方式传递的话,可以使用ref或者out关键字

From me: 引用类型在声明时在栈中分配一小片内存,new(即创建实例)的时候在堆中分配空间,堆上空间的地址保存在栈中分配的那一小片内存地址中。
值类型并不一定在栈中分配,由值类型所在的位置决定。

2、性能:

值类型通常被人们称为轻量级的类型,因为在大多数情况下,值类型的的实例都分配在线程栈中,因此它不受垃圾回收的控制,缓解了托管堆中的压力,减少了应用程序的垃圾回收的次数,提高性能。

所有的引用类型的实例都分配在托管堆上,c#中new操作符会返回一个内存地址指向当前的对象。所以当你在创建个一个引用类型实例的时候,你必须要考虑以下问题:

1、内存是在托管堆上分配的

2、在分配每一个对象时都会包含一些额外的成员(类型对象指针,同步块索引),这些成员必须初始化

3、对象中的其他字节总是设为零

4、在分配对象时,可能会进行一次垃圾回收操作(如果托管堆上的内存不够分配一次对象时)

性能:

1、在设计一个应用程序时,如果都是应用类型,那么应用程序的性能将显著下降,因为这会加大托管堆的压力,增加垃圾回收的次数。

2、虽然值类型是一个轻量级的类型,但是如果大量的使用值类型的话,也会有损应用程序的性能(例如下面要讲的装箱和拆箱操作,传递实例较大的值类型,或者返回较大的值类型实例)。

3、由于值类型实例的值是自己本身,而引用类型的实例的值是一个引用,所以如果将一个值类型的变量赋值给另一个值类型的变量,会执行一次逐字段的复制,将引用类型的变量赋值给另一个引用类型的变量时,只需要复制内存地址,所以在对大对象进行赋值时要避免使用值类型。

3、引用类型和值类型的区别。

1、引用类型和值类型都继承自Systerm.Object类。不同之处,几乎所有的引用类型都是直接从Systerm.Object继承,而值类型则是继承Systerm.Object的子类Systerm.ValueType类。System.ValueType本身是一个类类型,而不是值类型。其关键在于ValueType重写了Equals()方法,从而对值类型按照实例的值来比较,而不是引用地址来比较。

2、我们在给引用类型的变量赋值的时候,其实只是赋值了对象的引用;而给值类型变量赋值的时候是创建了一个副本(副本不明白?说通俗点,就是克隆了一个变量【修改对原来的变量没有影响】)。

三、堆栈空间分配

1.堆栈空间分配

①栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

②堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

你可能感兴趣的:(C#的栈与堆)