首先,我要求那些“过早优化”恐惧症的人免除我:我不想优化任何事情,我只是好奇。
我阅读/观察了两件事,包括在stackoverflow上(现在找不到链接):
我想知道编译器/ JIT /可以实际优化什么,以及什么不能。
以下是以下方法(假设变量实际上是使用的,因此无法对其进行优化):
// The method does many (useful) things, but these were cut here
public void myMethod() {
int var1 = 1;
... // do work
if (something) {
int var2 = 2;
int var3 = 3;
... // do work
}
int var4 = 4;
int var5 = 5;
... // do work
}
1。)编译器是否能够检测到var2
和var3
的空间是否可以重用var4
和var5
?我不记得在反汇编的字节码中看到过这样的事情。
2。)上面的代码方法是否等同于将方法结尾放到{}的情况?
public void myMethod() {
int var1 = 1;
... // do work
if (something) {
int var2 = 2;
int var3 = 3;
... // do work
}
{
int var4 = 4;
int var5 = 5;
... // do work
}
}
最后,让我们看一个更简单的案例:
public void myMethod() {
int var1 = 1;
... // do work, and then don't refer to var1 any more
int var4 = 4;
int var5 = 5;
... // do work
}
3.。)在这种情况下,var1
(或var4
)可以重用var5
个内存吗?即在这种情况下,该方法足以为两个本地int
变量提供内存,而不是三个。
(我知道理论上,这些是编译器的明显案例,但有时我会忽略一些事情,例如为什么编译器不能做或假设任何事情。)
答案 0 :(得分:3)
我可以代表真正的CPU编译器,我不确定JVM编译器,我不认为在编译级别代码的优化程度与你想象的一样(Java平台根本不关心太多关于记忆foorprint,你可以想象)。
对于真正的编译器,这些场景实际上经常被优化。这不是在高级语言级别,而是在RTL级别的较低中间级别。一切都是为了注册分配或堆栈分配,并通过计算函数内部变量的生存集来实现。
这意味着当编译代码时,通过假设任意数量的临时寄存器将所有内容转换为RTL,然后对于每个临时寄存器,通过所谓的live variable analysis计算其实时状态。这只是优化此类事物的方法之一。
例如
这是在将代码拆分为不包含跳转或标签的块之后完成的,这样您就可以确定任何指定块内的流程。
在这个计算之后,您可以很容易地看到大多数时间需要哪些变量,这些变量变得无用并且可以释放它们的预留空间等等。这样做是为了使您能够将尽可能多的变量放入寄存器中并大量优化汇编代码。
我个人认为 javac 不会做任何这样的事情(即使因为JVM是基于堆栈的,所以这只会破坏对象上的内存分配而现在没有任何需要),但我只是猜测
答案 1 :(得分:1)
是。堆栈插槽可以在嵌套}之后重用,我看过javac就这样做了。
是。案例1和案例2相同。第二个嵌套{}不会改变任何内容,除非有其后的内容。
Java编译器不会对此进行优化,尽管在理论上给出了它可以想到的所有正确条件。如果变量是最终的,则可能根本不为它们分配插槽。
您应该注意的另一件事是没有与嵌套}对应的字节代码指令,因此JVM和HotSpot没有基础知道嵌套作用域的结束位置,因此堆栈插槽的使用位置会改变。