有传言说ValueType被GC删除了(即GC释放了ReferenceType和ValueType)。
但事实上,并非如此。例如我们有代码:
class A {}
class B
{
void TestMethod()
{
A a = new A();
int x = 100;
}
}
在TestMethod( ) 方法的上下文(范围)中,创建类型A的对象 (ReferenceType)以及变量X (ValueType)。
方法完成后,变量X被销毁,类型A的对象失去对该对象的引用,并成为从GC中移除的竞争者。
换句话说,ValueType只要被执行就存在于上下文中,相应的Stack,通过Pop()方法的类型,会自己从内存中移除这个数据,而GC不参与this ,因此ValueType工作更快(尽管这完全取决于任务)。
问题是,它总是这样工作吗?(我读过各种文章,有时他们写道,只有当堆栈堵塞时才会发生这种情况,即达到满)
当堆栈几乎已满并且其中的所有数据(例如,都是对堆上的对象的引用)时,CLR会做什么?如何以及何时删除用户结构?CLR究竟是如何决定是从堆栈中删除数据还是让它存在第 N 次!??
ValueType 类型的局部变量 [在其上没有来自匿名方法和 lambda 的闭包] 直接位于堆栈或处理器寄存器中(正如优化器所希望的那样)。
您可以通过在调试中右键单击代码并选择 Go To Disassembly 来直接查看您的代码是如何执行的,也许这会清除一切。这是它在调试模式下的样子(关闭优化)。我在重要的地方添加了注释:
3 个寄存器的值被压入堆栈,因此它的指针现在与函数开始时的指针相差 12
esp 是指向堆栈开始的指针。将它减少 3Ch 就是在堆栈上为局部变量(或其他开销)分配 3Ch(60)字节,此时它已经与 ebp 中的值相差 12,因此局部变量在地址范围内从 [ebp-13] 到 [ebp-72]。它也是 [ebp-0Dh] 到 [ebp-48h]。
然后我们做了一堆检查并创建了一个漫长而痛苦的对象(这都是因为调试模式)。我将跳过大部分代码,它与问题无关:
最后将指向创建对象的指针放入栈中(ebp包含函数调用开始时栈的位置) 025B2E95 mov dword ptr [ebp-40h],eax
使用整数,它更容易 - 只需将所需的值推入相对 epb - 即 相对于函数执行时堆栈的开头。
现在是重点。我们将紧随其后的值加载到堆栈指针中
025B2E4D push ebx。本质上是这样esp = ebp-0Ch在下一行之后,我们得到 esp 的值等于函数开头的值。
由于什么,栈上的内存脱颖而出并被释放?
它通过将堆栈指针减少到所需的值而脱颖而出。释放——通过恢复指针的旧值。局部变量的销毁或“垃圾收集”没有成本。
这是 x86 上的标准机制,因此可以假设几乎总是如此。从函数返回时,堆栈指针的值将恢复为调用前的值。