谈论C#中堆和堆栈之间的区别(带有图表)
更新时间:2014年9月18日09:01:35贡献:hebedich
C#中的堆栈是在编译期间分配的内存空间,因此您的代码必须对堆栈的大小有清晰的定义。堆是程序运行时动态分配的内存空间,可以根据程序的运行状态来确定。要分配的堆内存大小
线程堆栈:称为堆栈堆栈
托管堆:简称堆
使用.Net框架开发程序时,我们不需要关心内存分配问题,因为大型管家GC会为我们处理所有事情。如果我们编写以下两段代码:
代码段1:
public int AddFive(int pValue)
{
int result;
result = pValue + 5;
return result;
}
代码段2:
public class MyInt
{
public int MyValue;
}
public MyInt AddFive(int pValue)
{
MyInt result = new MyInt();
result.MyValue = pValue + 5;
return result;
}
问题1:执行代码段1时,您知道pValue和结果如何存储在内存中吗?生命周期是多少?代码片段2呢?
为了解释上述问题,我们应该对堆栈和下面的托管堆(堆)(称为堆)有一个清晰的了解。如果要提高程序性能,则必须了解堆栈和堆!
本文将从堆栈,堆和类型变量开始,并分析我们编写的程序。
当C#程序在CLR上运行时,该存储器在逻辑上分为两个块:堆栈和堆。这两个基本元素构成了我们的C#程序的操作环境。
一,堆与堆:区别?
堆栈通常存储我们的代码执行的步骤,例如代码段1中的AddFive()方法,int pValue变量,int result变量等。另一方面,对象和数据大部分存储在堆。 (译者注:忽略编译器优化)我们可以将堆栈视为一个接一个地堆叠的盒子。使用它时,我们一次从顶部删除一个框。堆栈也是如此。当一个方法(或类型)被调用时,它是从栈顶(称为框架)开始,然后是下一个。堆不是这样。它就像一个仓库,其中存储了各种物品和我们使用的其他信息。与堆栈不同,它们在被调用后不会立即被清除。
图1,堆栈和堆的
(ͼ1)
堆栈内存不需要由我们管理,也不需要由GC管理。当堆栈的顶部元素用完时,将立即释放它。堆需要GC(垃圾收集:垃圾收集器)进行清理。
第二,将什么元素分配给堆栈?什么分配给堆?
执行程序时,堆栈和堆中分配了四种主要类型:值类型,引用类型,指针和指令。
值类型:
在C#中,从System.ValueType继承的类型称为值类型,主要有以下类型(在CLR 2. 0中支持的类型有所增加):
* bool
*字节
*字符
*小数点

*两倍
*枚举
*浮动
* int
*长
*字节
*短
*结构
* uint
*乌龙
* ushort
引用类型:
以下是从System.Object继承的引用类型:
*班级
*界面
*代表人
*对象
*字符串
指针:
在存储区中,对类型(通常称为“指针”)的引用由CLR(公共语言运行时)管理,我们无法显示它。应该注意的是,引用的类型(即指针)和引用的类型是两个完全不同的概念。指针占据了内存中的一个内存区域,它仅代表一个内存地址(或空值),而它指向的另一个内存区域就是我们的真实数据或类型。如图2所示:
(ͼ2)
命令:
说明将在以后介绍。
三,如何分配?
首先让我们看两点:
视图1,引用类型始终在堆上分配。 (正确吗?)
观点2,值类型和指针始终分配在定义它们的位置,它们不一定在堆栈上分配。 (这有点难以理解,您需要花点时间)
上面提到的堆栈,在程序运行时,每个线程(线程)都维护自己的专用线程堆栈。
调用方法时,主线程开始在其自己的程序集的元数据中找到被调用的方法,然后通过JIT快速对其进行编译,并将结果(通常是本地CPU指令)放在堆栈。 CPU通过总线从堆栈的顶部获取指令,驱动程序执行该指令。
下面通过示例详细讨论它。
我们在开头列出的摘录1:
public int AddFive(int pValue)
{
int result;
result = pValue + 5;
return result;
}
当AddFive方法开始执行时,方法参数(参数)将分配到堆栈中。如图3所示:
(ͼ3)
注意:该方法不存在于堆栈中,插图仅供参考。
接下来,该指令指向AddFive方法。如果是第一次执行该方法,则必须首先执行JIT即时编译。如图4所示:
(ͼ4)
当该方法开始在内部执行时,变量结果将分配到堆栈上,如图5所示:
(ͼ5)
该方法被执行,该方法返回,如图6所示:
(ͼ6)
执行该方法并返回后,将清除堆栈上的区域。如图7所示:
(ͼ7)
如上所述,通常在堆栈上分配一个值类型变量。您如何理解Viewpoint 2中的内容? “值类型和指针总是分配在定义它们的位置,而不必在堆栈上分配。”
原因是,如果在方法外部和引用类型中声明了值类型,则它将在堆上分配。
代码段2:
public class MyInt
{
public int MyValue;
}
public MyInt AddFive(int pValue)
{
MyInt result = new MyInt();
result.MyValue = pValue + 5;
return result;
}
当线程开始执行AddFive方法时,参数将分配到堆栈上,如图8所示:
(图片8)
由于MyInt是引用类型,因此将其分配在堆上,并在堆栈上生成一个指针(结果),如图9所示:
(图片9)
执行AddFive方法的情况如图10所示:
(ͼ10)
清除堆栈上的内存,并且堆仍然存在,如图11所示:
(ͼ11)
当程序需要更多的堆空间时,GC需要清理垃圾,挂起所有线程,查找所有无法访问的对象(即未引用的对象)并进行清理。并在地址排序后通知堆栈中的指针重新指向该对象。现在我们应该知道理解堆栈和堆对于我们开发高性能程序的重要性。当我们使用引用类型时,通常是对指针的操作,而不是引用类型对象本身。但是值类型可以自己操作。
接下来,我们将通过一个示例来说明这一点。
示例1:
public int ReturnValue()
{
int x = new int();
x = 3;
int y = new int();
y = x;
y = 4;
return x;
}
执行结果为3,略有修改:
示例2:
public class MyInt
{
public int MyValue;
}
public int ReturnValue2()
{
MyInt x = new MyInt();
x.MyValue = 3;
MyInt y = new MyInt();
y = x;
y.MyValue = 4;
return x.MyValue;
}
执行结果为4。
让我们分析原因。示例1与以下代码具有相同的效果:
public int ReturnValue()
{
int x = 3;
int y = x;
y = 4;
return x;
}
如图12所示,x和y分别占据堆栈上的一个存储区域,并且不会互相干扰。
(图1 2)
在情况2中,它与以下代码具有相同的作用:
public int ReturnValue2()
{
MyInt x;
x.MyValue = 3;
MyInt y;
y = x;
y.MyValue = 4;
return x.MyValue;
}如图13所示,
(ͼ13)
堆栈上的指针x和y指向堆上的同一区域。修改其中之一肯定会更改堆上的数据。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-373660-1.html
恬不知耻
折腾能让我们尽加快实现统一祖国的愿望