尽管我们不必担心.NET框架下的内存管理和垃圾回收(GarbageCollection),但我们仍应了解它们以优化我们的应用程序。同时,我们还需要具备一些有关内存管理工作机制的基础知识,可以帮助我们在日常程序编写中解释变量的行为。在本文中,我将解释堆栈和堆的基本知识,变量类型以及为什么某些变量可以以自己的方式工作。
在.NET Framework环境中,执行我们的代码时,内存中有两个地方可以存储代码。如果您以前不了解,那么让我向您介绍Stack and Heap。堆栈和堆都用于帮助我们运行代码。它们驻留在机器的内存中,并包含执行代码所需的所有信息。
*堆栈与堆:有什么区别?
堆栈负责保存我们的代码执行(或调用)路径,而堆栈负责保存对象(或数据,我们接下来将讨论很多堆问题)的路径。
将堆栈视为从上到下堆叠的一堆箱子。调用每个方法时,我们在堆栈顶部的框中记录应用程序中将要发生的情况,并且每次只能使用堆栈顶部的框。当堆栈顶部的框用完时,或执行该方法后,我们将丢弃此框并继续使用堆栈顶部的新框。堆的工作原理类似,但是大多数时候堆是用来保存信息而不是保存执行路径的,因此可以随时访问堆。与堆栈相比,堆栈没有访问限制。堆起来就像床上的旧衣服。我们没有花时间组织它。这是因为我们可以随时找到需要的衣服,而且它们的堆叠就像是储物柜中的堆叠式鞋子。盒子,我们只能从顶部盒子中取出它们,直到找到合适的盒子为止。

上面的图片不是内存中的真实表示,但是可以帮助我们区分堆栈和堆。
堆栈是自维护的,这意味着内存会自动维护堆栈。如果不再使用堆栈顶部的框,则将其抛出。相反,堆需要考虑垃圾回收。垃圾收集用于保持堆整洁。没有人愿意看到周围被盗的衣服。太臭了!
*堆栈和堆中有什么?
执行我们的代码时,堆栈和堆上主要放置四种数据类型:值类型,引用类型,指针和指令。
1.值类型:
在C#中,声明为以下类型的所有事物都称为值类型:
布尔
字节
字符
十进制
双
枚举
浮动
int
长
字节
短
结构
uint
阿龙
ushort
2.参考类型:
声明为以下类型的所有事物都称为引用类型:
班
界面
委托
对象
字符串
3.指针:
放置在内存管理方案中的第三种类型是类型引用。引用通常是一个指针。我们不会显式使用指针,它们是由公共语言运行库(CLR)管理的。指针(或引用)与引用类型不同,因为当我们说某物为引用类型时,这意味着我们通过指针进行访问。指针是一个存储空间,它指向另一个存储空间。就像堆栈和堆一样,指针也占用内存空间,但是它的值是内存地址或null。

4.命令:
在以后的文章中,您将看到说明的工作原理...
*如何决定将其放置在何处?
这里有一条黄金法则:
1.引用类型始终在堆上。 (足够简单吗?)
2.值类型和指针始终放在声明它们的位置。 (这有点复杂。您需要先知道堆栈的工作原理,然后才能知道它的声明位置。)
正如我们前面提到的,堆栈负责在执行(或调用)代码时保存路径。当我们的代码开始调用方法时,它将在代码中放置一条编码指令(在该方法中),然后放置该方法的参数,然后代码将执行到该方法中被“推”到的变量位置。堆栈的顶部。通过以下示例很容易理解...
这是一个方法(方法):
public int AddFive(int pValue)
{
int结果;
结果= pValue + 5;
返回结果;
}
让我们看一下现在栈顶发生的情况,并记住实际上还有许多其他事情已被推送到我们正在观察的栈顶下方。
首先,将方法(仅包含需要执行的逻辑字节,即执行该方法的指令,而不是方法主体中的数据)推入堆栈,然后将方法参数推入堆栈。 (我们稍后将讨论更多的参数传递)

接下来,将控制(即执行该方法的线程)传递给堆栈上的AddFive()指令,

执行该方法时,我们需要为堆栈上的“结果”变量分配一些内存,

方法完成执行并返回我们的结果。
方法执行完成,然后返回方法的结果。

通过将堆栈指针指向AddFive()方法使用的可用内存地址,此方法在堆栈上使用的所有内存将被清除,程序将自动返回到堆栈上的原始方法调用位置(在此示例中未看到)。

在此示例中,我们的“结果”变量被放置在堆栈上。实际上,当在方法主体中声明值类型数据时,它们都将放置在堆栈中。
有时将值类型数据放在堆中。请记住,此规则值类型始终放置在声明它们的位置。好吧,如果值类型数据在方法外声明并且存在于引用类型中,那么它将被堆中的引用类型替换。
再看一个例子:
假设我们有一个MyInt类(它是一个引用类型,因为它是一个类类型):
公共类MyInt
{
publicint MyValue;
}
然后执行以下方法:
公共MyInt AddFive(int pValue)
{
MyInt结果=新的MyInt();
result.MyValue = pValue + 5;
返回结果;
}
如前所述,该方法及其参数放置在堆栈上,然后将控制传递给堆栈上的AddFive()指令。

会有一些有趣的现象……
因为“ MyInt”是引用类型,它将被放置在堆上,并且将同时在堆栈上生成对该堆的指针引用。

执行AddFive()方法后,我们将清空...

我们将寂寞的MyInt对象留在堆中(堆栈上将没有指向MyInt对象的指针!)

这是垃圾收集器(以下称为GC)的工作位置。当我们的程序达到特定的内存阈值并且我们需要更多的堆空间时,GC开始生效。 GC将停止所有正在运行的线程,在堆中查找主程序不再访问的所有对象,然后将其删除。然后,GC将重组堆中所有剩余的对象以节省空间,并调整与堆和堆中这些对象有关的所有指针。您肯定会认为此过程非常消耗性能,因此这时您将知道为什么我们需要特别注意堆栈和堆中的内容,尤其是当我们需要编写高性能代码时。
好的...太好了,对我有什么影响?
好问题。
使用引用类型时,实际上是在处理该类型的指针,而不是类型本身。当我们使用值类型时,我们使用的是值类型本身。听起来很困惑,对吧?
类似地,一个例子是最好的描述。
假设我们执行以下方法:
public int ReturnValue()
{
int x = new int();
x = 3;
int y = new int();
y = x;
y = 4;
返回x;
}
我们将获得值3,这很简单,对吧?
如果我们首先使用MyInt类
公共类MyInt
{
public int MyValue;
}
然后执行以下方法:
public int ReturnValue2()
{
MyInt x = new MyInt();
x.MyValue = 3;
MyInt y = new MyInt();
y = x;
y.MyValue = 4;
返回x.MyValue;
}
我们将得到什么? ... 4!
为什么? ... x.MyValue如何变成4? ...看一下我们所做的事情,您会知道发生了什么事情:
在第一个示例中,一切按计划进行:
public int ReturnValue()
{
int x = 3;
int y = x;
y = 4;
返回x;
}

在第二个示例中,我们没有得到“ 3”,因为变量“ x”和“ y”都指向堆中的同一对象。
public intReturnValue2()
{
MyInt x;
x.MyValue = 3;
MyInt y;
y = x;
y.MyValue = 4;
返回x.MyValue;
}

我希望以上内容将使您对C#中的值类型和引用类型之间的基本区别有更好的了解,并对指针和何时使用它们有一定的基本了解。在本系列的下一部分中,我们将深入研究内存管理,并专门讨论方法参数。
博客原始文本:
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-373656-1.html
假的
外面的墙上都有
支持国产企业