ie int num = 10; //num is the location of the 10 in memory?
实际上,它在堆栈中。
至少,局部变量和参数是。所以,在:
public void foo() {
int x = 10;
System.out.println(x);
int y = 20;
System.out.println(y);
}
会发生什么(编译上面的代码,然后运行javap -c 来检查字节码并继续),javac 会将它编译成如下所示的字节码:
STARTMETHOD foo SLOTS=1
LOADC 0, 10 # load constant value '10' into the first slot (slot 0)
PUSH # Push this on the stack.
INVOKESTATIC java/lang/System println(int)
LOADC 0, 20 # load constant value '20' into... the first slot!
PUSH
INVOKESTATIC java/lang/System println(int)
注意:这过于简单化了; System.out.println 实际上是一个GETFIELD,然后是一个接口调用结果,以及更多这样的简化。但是与栈和槽相关的位代表了它是如何工作的。
你在这里看到了一些有趣的东西。值得注意的是,局部变量与内存位置不是一对一的匹配。 Javac 意识到当你创建 y 时你从不使用 x,所以它只是重复使用同一个 slot。
那个“插槽”的东西被声明在堆栈上。堆栈是内存,但它不断被重用。堆栈是一小块内存(想想 1MB 左右),每次调用一个方法时,你来自哪里,它拥有的所有状态(本地变量等)都存在那里,所有新状态(新方法的本地变量,例如这些插槽)被放在“顶部”。然后当你从一个方法返回时,那个“指向栈顶的指针”正好回到它原来的位置,所以你调用的下一个方法会立即覆盖这些东西。
那么,num 是“内存地址”吗?不是真的,因为地址实际上是“0”(第一个插槽)。这不会转化为“我们堆栈上的第 0 个项目”。它转换为“进入此方法时的堆栈顶部”,将其称为“内存位置”有点牵强。
在任何情况下,这个变量都不存在于堆上根本 - 堆栈与堆是分开的。
对于字段,情况有所不同。给定:
class Foo {
int num = 10;
Object o = new Object();
}
这是完全不同的。 Foo 的实例由一堆堆上的内存表示。具体来说,一些字节来注册这是一个 Foo 实例,然后是一些字节来存储“10”(字面意思是,0x00 00 00 10 显示在内存中,可能是 0x10 00 00 00,具体取决于事物的字节序 - 如果你 coredump 进程内存空间),然后是一些字节来存储一个 'ref' 到那个对象 o。
对该字段的引用永远不会存储为内存地址。它存储为此 Foo 实例的“引用”,VM 将“获取num 字段的值”翻译为“相对于实例所在位置的第 10 个偏移量”。
如果您想将其称为内存地址,请成为我的客人。但是这样规定有什么意义呢?
'ref' 就像一个指针,除了它通常不是直接的内存地址,而是 JVM 可以用来确定内存地址的某个数字。通常对象在 8 字节边界上对齐,因此在某些 VM 上,这实际上存储了目标内存地址的 8 分之一,或者它是相对于某物的内存地址,或者它只是稍后查找的键。这取决于 VM 和 GC 实现,了解这些东西的理由为零,你无法从 java 代码中观察到任何这些。