在 Java 中是这样的:
public void method() {
if (condition) {
Object x = ....;
}
System.out.println(x); // Error: x unavailable
}
我想知道的是:x
仅限于if
-statement 范围的事实只是 Java 编译器的一个功能,还是x
在 -statement 之后实际上从堆栈中if
删除?
不,代码块没有单独的堆栈帧,使用其中一种环绕方法。
但是,一旦变量离开作用域,它在当前堆栈帧中的位置就可以重新用于其他变量。
Java Virtual Machine Specification § 3.6 Frames中描述了堆栈帧的结构和使用:
每次调用方法时都会创建一个新框架。框架在其方法调用完成时被销毁,无论该完成是正常的还是突然的(它会引发未捕获的异常)。
这明确指定了方法调用和帧之间的 1:1 关系。
首先,在字节码中变量是存储在变量槽和变量槽中而不是在栈上。插槽可以被另一个变量重用,但不能保证值会从变量插槽中删除。
例如下面的类
public class A {
public void method(boolean condition) {
6 if (condition) {
7 Object x = "";
8 System.out.println(x);
9 }
10 System.out.println(condition);
}
}
编译成这个字节码:
// class version 50.0 (50)
public class A {
...
// access flags 0x1
public method(Z)V
L0
LINENUMBER 6 L0
ILOAD 1
IFEQ L1
L2
LINENUMBER 7 L2
LDC ""
ASTORE 2
L3
LINENUMBER 8 L3
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 2
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
L1
LINENUMBER 10 L1
FRAME SAME
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ILOAD 1
INVOKEVIRTUAL java/io/PrintStream.println (Z)V
L4
LINENUMBER 11 L4
RETURN
L5
LOCALVARIABLE this LA; L0 L5 0
LOCALVARIABLE condition Z L0 L5 1
LOCALVARIABLE x Ljava/lang/Object; L3 L1 2
MAXSTACK = 2
MAXLOCALS = 3
}
请注意,在第 7 行创建的变量 x 存储在变量 slot 2 中,该变量在第 10 行对应的字节码中仍然可用。
没有关于如何将 Java 语言编译成字节码的规范,除了几个如何正确编译某些语言结构的示例。然而,Java 编译器允许删除未使用的变量。例如,如果 x 已分配,但未在任何地方使用,则允许编译器删除该代码。同样,编译器内联所有静态常量。
是的,它确实从堆栈中删除,使得以前被“x”占用的插槽可以被其他一些局部变量重用。