4

首先,我请那些有“过早优化”恐惧症的人放过我:我不想优化任何东西,我只是好奇

我阅读/观察了两件事,包括在 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.) 编译器是否能够检测到 and 的空间var2可以var3用于var4and 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变量的内存就足够了,而不是三个。

(我知道理论上,这些对于编译器来说是显而易见的情况,但有时我会忽略一些事情,例如为什么编译器不能做或假设任何事情。)

4

2 回答 2

3

我可以为真正的 CPU 的编译器说话,我不确定 JVM 编译器,而且我认为在编译级别的代码没有你想象的那么优化(Java 平台根本不关心内存布局,因为你可以想象)。

对于真正的编译器,这些场景实际上经常被优化。这不是在高级语言级别而是在较低的中间级别(如RTL级别)完成的。一切都是为了寄存器分配或堆栈分配而制作的,并且通过计算函数内部变量的活动集来工作。

这意味着在编译代码时,所有内容都通过假设任意数量的临时寄存器转换为 RTL,然后对于每个临时寄存器,通过所谓的实时变量分析计算其实时状态。这只是优化此类事情的方法之一。

例如

  • var1 live 范围是从指令 1 到 10
  • var2 live 范围是从指令 5 到 17
  • var3 live 范围是从指令 3 到 25
  • 等等

这是在将代码拆分为不包含跳转或标签的块之后完成的,这样您就可以确定任何指定块内的流程。

在这个计算之后,你可以很容易地看到哪些变量在大多数时候是需要的,哪些变得无用并且可以释放它们的保留空间等等。这样做是为了让您能够将尽可能多的变量放入寄存器并优化汇编代码。

就我个人而言,我不认为javac做任何这些(即使因为 JVM 是基于堆栈的,所以这只会破坏对象上的内存分配,而现在没有任何需要),但我只是猜测。

于 2013-01-05T04:27:32.140 回答
1
  1. 是的。嵌套的 } 之后可以重用栈槽,我见过 javac 这样做的。

  2. 是的。情况 1 和 2 是等价的。第二个嵌套的 {} 不会改变任何东西,除非它后面有什么东西。

  3. Java 编译器不会对此进行优化,尽管理论上它可以想象到所有正确的条件。如果变量是最终的,则可能根本不为它们分配插槽。

您应该注意的另一件事是,没有与嵌套的 } 对应的字节码指令,因此 JVM 和因此 HotSpot 没有基础知道嵌套范围的结束位置以及堆栈槽更改使用的位置。

于 2013-01-05T04:34:01.430 回答