7

在 Java 中是这样的:

public void method() {
  if (condition) {
    Object x = ....;
  }
  System.out.println(x); // Error: x unavailable
}

我想知道的是:x仅限于if-statement 范围的事实只是 Java 编译器的一个功能,还是x在 -statement 之后实际上从堆栈中if删除?

4

4 回答 4

7

不,代码块没有单独的堆栈帧,使用其中一种环绕方法。

但是,一旦变量离开作用域,它在当前堆栈帧中的位置就可以重新用于其他变量。

Java Virtual Machine Specification § 3.6 Frames中描述了堆栈帧的结构和使用:

每次调用方法时都会创建一个新框架。框架在其方法调用完成时被销毁,无论该完成是正常的还是突然的(它会引发未捕获的异常)。

这明确指定了方法调用和帧之间的 1:1 关系。

于 2010-06-17T11:31:13.893 回答
2

块是 Java 语言(一种结构化编程语言)的一部分,而不是编译后的字节码(一种非结构化语言)的一部分。

类文件中的方法规范指定了该方法总共使用了多少个局部变量,高于实际的指令列表。但是无法从字节码中推断出 Java 代码中的块在哪里。

于 2010-06-17T11:33:44.647 回答
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 已分配,但未在任何地方使用,则允许编译器删除该代码。同样,编译器内联所有静态常量。

于 2010-06-17T11:55:42.817 回答
-1

是的,它确实从堆栈中删除,使得以前被“x”占用的插槽可以被其他一些局部变量重用。

于 2010-06-17T11:35:33.130 回答