这篇博文,只是引出,确实存在内存管理,垃圾回收这个事,以及认识堆,栈区别而已,
重点一句话:
栈负责保存我们的代码执行(或调用)路径,而堆则负责保存对象(或者说数据,接下来将谈到很多关于堆的问题)的路径。
可以简单理解,栈是抽屉一层层的,堆就是床上衣服,栈是自行维护的,也就是说内存自动维护栈,当栈顶的盒子不再被使用,它将被抛出。相反的,堆需要考虑垃圾回收
第二点: 栈,堆里到底有什么?
当我们的代码执行的时候,栈和堆中主要放置了四种类型的数据:值类型(Value Type),引用类型(Reference Type),指针(Pointer),指令(Instruction)。
关于具体存放内容,记住以下两点
1. 引用类型总是放在堆中。
2. 值类型和指针总是放在它们被声明的地方。
就像我们先前提到的,栈是负责保存我们的代码执行(或调用)时的路径。
当我们的代码开始调用一个方法时,将放置一段编码指令(在方法中)到栈上,紧接着放置方法的参数,然后代码执行到方法中的被“压栈”至栈顶的变量位置。
第三点:值类型,引用类型到底什么区别,和垃圾回收有什么具体关系呢?
具体可以通过以下代码例子,加深理解
下面是一个方法(Method):
public int AddFive(int pValue)
{
int result;
result = pValue + 5;
return result;
}
首先方法(只包含需要执行的逻辑字节,即执行该方法的指令,而非方法体内的数据)入栈,紧接着是方法的参数入栈
接着,控制(即执行方法的线程)被传递到堆栈中AddFive()的指令上,
当方法执行时,我们需要在栈上为“result”变量分配一些内存,
方法执行完成,然后方法的结果被返回。
通过将栈指针指向AddFive()方法曾使用的可用的内存地址,所有在栈上的该方法所使用内存都被清空,且程序将自动回到栈上最初的方法调用的位置
在这个例子中,我们的"result"变量是被放置在栈上的,事实上,当值类型数据在方法体中被声明时,它们都是被放置在栈上的。
值类型数据有时也被放置在堆上。记住这条规则--值类型总是放在它们被声明的地方。好的,如果一个值类型数据在方法体外被声明,且存在于一个引用类型中,那么它将被堆中的引用类型所取代。
我们来看一下对比的例子,
假如我们有这样一个MyInt类(它是引用类型因为它是一个类类型):
public class MyInt
{
publicint MyValue;
}
然后执行下面的方法:
public MyInt AddFive(int pValue)
{
MyInt result = new MyInt();
result.MyValue = pValue + 5;
return result;
}
前面方法指令,参数入栈都是一样的,区别在于
由于"MyInt"是一个引用类型,它将被放置在堆上,同时在栈上生成一个指向这个堆的指针引用。
这个引用变量本身,以及对应的值(内存地址)仍在栈上,只不过具体对象数据,放在堆上
同样的,栈上在AddFive()方法被执行之后, 栈上和这个方法相关的内存,被自动释放,
很明显,堆中对象所占内存空间,仍然孤独留在堆分配的内存中,这就是垃圾回收器(后简称GC)起作用的地方。
当我们的程序达到了一个特定的内存阀值,我们需要更多的堆空间的时候,GC开始起作用。GC将停止所有正在运行的线程,找出在堆中存在的所有不再被主程序访问的对象,并删除它们,然后GC会重新组织堆中所有剩下的对象来节省空间,并调整栈和堆中所有与这些对象相关的指针。
不用说,这个过程非常耗费性能,
同时也回应一个问题,为什么称为高程需要重视栈和堆。
总结如下:
第一:栈内存是自行维护的,堆需要借助垃圾回收,
所以栈内存,申请,释放相对,堆肯定要快
第二:方法在栈中执行,依次是入栈,方法指令,参数,局部变量,
本文参考
https://www.c-sharpcorner.com/article/C-Sharp-heaping-vs-stacking-in-net-part-i/