Java字节码中的多个静态块会发生什么情况?

Java字节码中的多个静态块会发生什么情况?,java,bytecode,static-block,Java,Bytecode,Static Block,这里是一个非常基本的Java应用程序,只包含一个类。在该类中,有一个main方法和两个static块 class Test { public static void main(String args[]) { System.out.println("Main"); } static { int a = 10; } static { int a = 20; } } 下面是编译这个应用程序产生的字节

这里是一个非常基本的Java应用程序,只包含一个
。在该类中,有一个
main
方法和两个
static

class Test {
    public static void main(String args[]) {
        System.out.println("Main");
    }

    static {
        int a = 10;
    }

    static {
        int a = 20;
    }
}
下面是编译这个应用程序产生的字节码。我不明白静态块发生了什么:

static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=1, args_size=0
         0: bipush        10
         2: istore_0
         3: bipush        20
         5: istore_0
         6: return
      LineNumberTable:
        line 34: 0
        line 37: 3
        line 38: 6

我的问题是:第二个静态块在哪里?如果它们合并,那么JVM如何区分两个块包含的变量,因为两个块都有相同名称和类型的变量?

在这种情况下,您可以看到两个块仍然存在。常数10和常数20分别出现在不同的行上。但是,这些块是“合并”的,因为它们只是按顺序执行。由于您没有对所讨论的变量执行任何操作,所以这两个变量都只是写入堆栈顶部(我认为这就是
istore\u 0
所做的),然后被忽略

Code:
  stack=1, locals=1, args_size=0
     0: bipush        10
     2: istore_0
     3: bipush        20
     5: istore_0
     6: return
编辑:
istore\u 0
将值存储到局部变量。两个
a
都是相同的变量。这是因为它们不是在同一时间使用的,所以编译器只是试图提高效率,并为不在同一时间使用的变量重用堆栈空间


从概念上讲,两者都是不同的变量。如果以后可以使用第一个
a
的值,编译器将永远不会这样做。但在物理上,它们已经合并以节省空间。这只是一个简单的内存优化。

在字节码级别,每个类只有一个静态初始化方法,名为
。Java级别的所有静态初始值设定项都将编译成一个方法。(这包括静态{}块和使用非常量表达式初始化的任何静态字段,即
静态Foo Foo=bar()

至于JVM如何区分变量,它没有。字节码的抽象级别低于Java源代码。没有局部变量,只有一个堆栈和一个可以保存值的插槽表。JVM不需要知道哪些值是执行代码的值,它只需要执行字节码所说的

唯一相关的时间是您是否需要用于调试、反射等的元数据。默认情况下,编译器将包含元数据,说明字节码中的每个插槽也对应于哪个局部变量(如果有的话)。在这种情况下,每个插槽由多个局部变量使用,因此它存储字节码的范围,在此期间,插槽保存每个源级别局部变量的值


无论如何,这都是静态初始值设定项。字节码级别根本不存在非静态初始值设定项。相反,所有初始值设定项都被连接并内联到调用超级构造函数的每个构造函数(
类的方法)中。这意味着代码可能会出现多次。

变量不同,但编译器通常会“合并”不同时使用的变量(甚至只是堆栈空间)。如果每个变量占用一个“插槽”,并且两个这样的插槽从未同时使用,那么编译器将只将两个插槽合并到一个插槽中以节省空间。如果它合并两个块,那么为什么它只显示一个局部组件<代码>代码:stack=1,locals=1,args_size=0我不知道你的意思。所有静态块都是按顺序执行的,因此将它们连接成一个更大的块是非常有效的。只有一个局部变量,因为一次只使用一个局部变量。我不确定“组件”是什么。好吧,我同意你的观点,但如果我们创建变量a作为第一个静态块的
final
,那么它如何使用相同的堆栈空间呢?同样,因为它是一个局部变量。它不能在代码块之外使用。如果你想看到两个变量,你必须把它们放在同一个代码块中,并对它们进行处理(比如打印它们)。你是说Java编译器不会合并两个静态块吗?