例如:
class Test{
int a;
public Test(){
a=10;
}
};
// somewhere else
Test t = new Test();
a
是在运行时还是编译时分配的?如果是在编译时,因为Java运行于直接执行已编译的.class文件的虚拟机上,这怎么可能呢?
另外:
a
何时被赋值为10
?对于引用变量
t
,它是如何工作的?
谢谢。
class Test{
int a;
public Test(){
a=10;
}
};
// somewhere else
Test t = new Test();
a
是在运行时还是编译时分配的?如果是在编译时,因为Java运行于直接执行已编译的.class文件的虚拟机上,这怎么可能呢?
另外:
a
何时被赋值为10
?
对于引用变量t
,它是如何工作的?
谢谢。
编译时不会发生内存分配,只有在加载和运行时才会。
编译时只生成.class文件。
请记住您需要一个主类来运行程序。当您使用Java并且将类路径设置为.class文件运行程序时,会出现加载和链接等步骤。
类加载器将文件加载到permgen(永久代)中。
当调用主方法时,会创建堆栈并将本地变量放置在其中。
当运行时遇到new关键字时,它会在堆上创建对象,并分配所需的内存,例如Test所需的内存。
a
是一个原始类型,而不是对象或引用。 - Peter Lawrey本地变量和方法参数(如原始类型或引用)在编译时被指定为在堆栈上分配位置。
在运行时,这并不能保证反映它在内存中的布局。
只有在运行时才会在堆上分配对象。
Java 运行在直接处理编译 .class 文件的虚拟机上,那么如何可能呢?
只有虚拟机知道代码将如何编译,因此不可能在编译时进行内存分配。
当一个值被分配为 10 时?
在赋值发生的那一行。如果它没有使用,JIT 可以丢弃它,所以可能根本不会发生。
对于引用变量 t,同样的问题也存在。
t
在对象构造后的 =
之后被分配。
javac
编译器可以分配/赋值常量、指令、寄存器、堆栈使用,但在运行时 JIT 可以重新优化它以适应实际的硬件,无论如何都可以。也就是说,在运行时机器码可能看起来完全不同。 - Peter Lawrey这个问题有点棘手,我不确定你是否能从整个讨论中得到你想要的确切答案,因为实际上,你所问的是编译器内部的主要话题,而大多数人并不关心。
在大多数情况下,Java编译器使用自动内存管理,因此真正做出决定的是编译器本身,这也可能会随着版本的变化而改变。
但在我开始解释之前,我想澄清一下我的符号表示:
在Java中,String是一个特殊情况,虽然它是一个对象,但它可能与其他对象不同。
[object]有一个特殊属性。它有一个值和一个标识符,标识符解析为值的过程以及发生的时间取决于绑定类型。
有静态绑定,可以在编译时解析绑定,并且在编译时已知其值或方法。这也称为“早期”绑定。例如:
int a = 0; // AND //像print()这样的直接函数调用;
还有动态绑定,其中标识符和值之间的绑定或子程序到程序的绑定直到运行时才能发生。这也称为“晚期”绑定。例如:
public void foo(java.util.List list) { list.add("bar"); }
还有一种混合类型的绑定,但我不会谈论它,因为我没有发现Java有它。
现在,绑定也与作用域密切相关,即变量“存在”于某个特定的作用域中。这是一个我真的不想深入探讨的话题(作用域有点棘手),这会使这篇文章成为一部小说而不是一部中篇小说。
Java中内存分配的方式取决于几个因素:
如果在编译时已知对[Object]、[object]或[primitive]的引用,并且可以使用静态绑定,则编译器可能会在编译时为这些对象(注意我没有使用括号)分配内存。
如果在编译时无法知道对[Object]、[object]或[primitive]的引用,并且必须使用动态绑定,则编译器可能会在运行时为这些对象分配内存。
Java处理在运行时分配的对象的方式取决于使用了哪种类型的绑定。
总之,不要担心它。这对你来说是一个大头痛。
如果我对任何事情都错了,请有人告诉我。我有点生疏。