在网络上搜索后,我还没有找到关于实例变量在 Java 内存模型中的确切位置的良好而全面的答案。例如我们有这段代码(带有变量的阴影声明):
class A {
int var = 1;
void m() {
System.out.println("\'m()\' is called from class A");
}
}
class B extends A {
int var = 5;
void m() {
System.out.println("\'m()\' is called from class B");
}
}
public class Class1 {
public static void main(String args[]) {
A aref = new B();
aref.m();
String s = (aref.var)==1?"A":"B";
System.out.println("\'var\' is called from class " + s);
}
}
这段代码的输出是:
'm()' is called from class B
'var' is called from class A
现在的问题不是继承在 Java 中是如何工作的,而是这个实例变量在 Java 内存模型中的什么位置?请论证你的答案。
谢谢
最佳答案
一个对象被保存在堆中,但是作为一个内存块等于所有变量的大小加上一些额外的字节来存储虚拟方法表(VMT)和对象的原型(prototype)位置(可能更多取决于JVM 实现)。
因此您的示例对象在(32 位)内存中看起来像这样(指针值仅用于演示):
[0000] 0154 // pointer to prototype
[0004] 3625 // pointer to virtual method table
[0008] 0001 // int var
现在在上面的例子中,没有成员被访问,所以 JVM 所做的就是找到那个原型(prototype)的 VMT,跳转到写在那里的函数地址并执行它。
如果您的 var = 1
代码实际上通过了优化器,生成的汇编代码将不知道这个“var”的东西,而是使用直接内存访问。像这样:
set [4924 + 8], 1
其中 4924 是实例的内存位置,+8 是变量 var
的偏移量。请记住,这是(使人类可读的)程序集 而不是字节码,所以基本上 JIT 完成后剩下的就是它。
由于您的两个对象大小相同,因此甚至可以将 A“向上转换”为 B,但它不起作用的唯一原因是因为 Java 禁止此类不安全操作。在其他安全性较低的语言(如 C++)中,您可以轻松做到这一点,而且很可能会侥幸逃脱。
关于java - Java 内存模型中的实例变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19982741/