CLR - 引用地址存储在哪里?
我是编程新手,这给我带来了很多困惑。
假设我们有以下语句:
Int32 i = 1;
i
的内容存储在内存中,这将是四个字节: 00000000 00000000 00000000 00000001
CLR 稍后如何访问这个内存位置? CLR 是否将该内存块的地址存储在某处?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
System.Int32
是值类型,不使用引用。事实上,如果编译器能够找到一个 CPU 寄存器来在其整个生命周期中保存局部变量,那么它可能永远不会在内存中。
如果它在内存中,则通过向堆栈指针(
ESP
)或引用类型(C# 中的class
)的地址添加偏移量来找到其地址包含它的对象。在 JIT 生成的代码中,值类型的变量与本机代码使用的变量没有区别(没有对象头或类似的东西)。
System.Int32
is a value type, no references used.In fact, a local variable may never be in memory at all, if the compiler can find a CPU register to hold it during its entire life.
If it is in memory, its address will be found by adding an offset to the stack pointer (
ESP
) or to the address of the reference-typed (class
in C#) object that contains it.In the code generated from the JIT, variables of value types are indistinguishable from variables used by native code (there's no object header or anything like that).
编译器会跟踪变量的位置,以便它可以创建以正确方式访问它的代码。
在这种情况下,您声明了一个局部变量,因此它将在堆栈上分配。
程序不会访问特定地址处的变量,而是作为基指针的偏移量,基指针指向当前方法的堆栈帧。
例如,将变量设置为 1 的代码在编译为 32 位应用程序的机器代码时可以如下所示:
ebp
寄存器指向堆栈帧的顶部,因此在本例中,i
变量分配的字节数比该变量低 8 个字节。The compiler keeps track of where the variable is, so that it can create code that will access it in the right way.
In this case you are declaring a local variable, so it will be allocated on the stack.
The program will not access the variable at a specific address, but as an offset from the base pointer, which points to the stack frame of the current method.
The code to set the variable to 1 can for example look like this when it's compiled into machine code for a 32 bit application:
The
ebp
register points to the top of the stack frame, so thei
variable is allocated eight bytes below that in this case.假设通常情况下,如果
i
是局部变量,它将保存在堆栈中。 .NET 的抽象虚拟机是基于堆栈的虚拟机。我要补充一点,在 Intel/AMD 上
i
不会以这种方式保存:-) Intel/AMD 是小端。所以它将是00000001 00000000 00000000 00000000
我将其混合一点...现在...IL 语言和 .NET abstract VM 是“纯”堆栈基于,所以有一个堆栈:-)(但没有寄存器,所以“纯粹”)(我希望你知道什么是堆栈)。当代码被 JITted 为您正在使用的计算机的机器代码时,
i
可能会被放入寄存器或堆栈中。请注意,一般说值类型(或非引用类型,如果您想包括托管/非托管指针/引用)保存在堆栈和/或寄存器中是错误的错误。他们在哪里得救就得救。例如,类的值类型成员与类一起保存(因此通常在堆中)。 yield 函数、异步函数、“普通”方法中但被“闭包类型”匿名函数引用的值类型通常保存在堆中的某个位置。但所有这些都是参考实现细节。
Let's say that NORMALLY, if
i
is a local variable, it will be saved on the stack. The abstract VM of .NET is a stack based one.I'll add that on Intel/AMD
i
won't be saved that way :-) Intel/AMD are little endian. So it will be00000001 00000000 00000000 00000000
I'm mixing it a little... Now... the IL language and the .NET abstract VM are "pure" stack based, so there is a stack :-) (but there are no registers, so "pure") (I hope you know what a stack is). When the code is JITted to machine code for the computer you are using, probably
i
will be put in a register or on the stack.Note that in general it's wrong wrong wrong to say that value types (or non-reference types, if you want to include managed/unmanaged pointers/references) are saved on the stack and/or in registers. They are saved where they are saved. For example value types member of a class are saved with(in) the class (so normally in the heap). Value types in yield functions, in async functions, in "normal" methods but being referenced by "closure type" anonymous functions are normally saved somewhere in the heap. But all these are reference implementations details.
据我所知,实际的堆分配引用存储为双指针(或某种等效的指针),以便垃圾收集器可以移动内存,而不必通过更新指向的指针来影响代码中引用某些内容的任何地方。
From what I can tell, actual heap allocated references are stored as double pointers (or some equivalent) so that the garbage collector can move memory without having to affect anywhere in code that something is referenced by updating pointer being pointed to.