2

Here’s a very basic application in Java, containing only one class. In that class, there is one main method and two static blocks.

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

    static {
        int a = 10;
    }

    static {
        int a = 20;
    }
}

And here’s the bytecode produced by compiling this application. I don’t understand what has happened with the static blocks:

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

My question is: where is the second static block? If they merge, then how can the JVM differentiate between variables contained by both blocks, because both blocks have variables with same name and type?

Ry-
  • 218,210
  • 55
  • 464
  • 476
  • 3
    The variables are different, but it's common for a compiler to "merge" variables (or even just stack space) that aren't used at the same time. If each variable takes one "slot" and two such slots are never used at the same time, then the compiler will just merge the two slots into one slot to save space. – markspace Apr 10 '18 at 02:09

2 Answers2

2

In this case you can see both blocks are still there. The constant 10 and the constant 20 appear separately on different lines. However the blocks are "merged" in the sense that they just execute sequentially. Since you don't do anything with the variables in question, both are just written to the top of stack (I think that's what istore_0 does) and then ignored.

Code:
  stack=1, locals=1, args_size=0
     0: bipush        10
     2: istore_0
     3: bipush        20
     5: istore_0
     6: return

Edit: istore_0 stores the value to a local variable. Both a are the same variable. That's because they aren't used at the same time, so the compiler just tries to be efficient and reuse stack space for variables that aren't used at the same time.

Conceptually, both are different variables. The compiler would never do this if somehow the value of the first a could be used later. But physically they've been merged to save space. It's just a simple memory optimization.

markspace
  • 10,621
  • 3
  • 25
  • 39
  • If it merge both blocks, then why it shows only one local component? `Code: stack=1, locals=1, args_size=0` –  Apr 10 '18 at 02:20
  • I'm not sure what you mean. All static blocks just execute sequentially, so it's efficient to concatenate them into one larger block. There's only one local variable because there's only one local variable used at a time. I'm not sure what a "component" is. – markspace Apr 10 '18 at 02:21
  • OK, I agree with you, but if we create `variable a` as `final` of first static block, then how it can use same stack space? –  Apr 10 '18 at 02:29
  • Again because it's a local variable. It can't be used outside of the code block. If you want to see two variables, you have to put both in the same code block, and do something with them (like maybe print them). – markspace Apr 10 '18 at 02:30
  • Did you mean Java compiler will not merge both static blocks? –  Apr 10 '18 at 02:34
  • It'll still merge the blocks, but if two variable are used differently at the same time it won't merge them. – markspace Apr 10 '18 at 02:45
  • Does it uses two different local blocks `{...}` in side merged static block to differentiate both? –  Apr 10 '18 at 02:48
  • I'm not really sure that the question is. Blocks inside braces `{}` are an artifact of higher level languages, they don't appear in machine code. – markspace Apr 10 '18 at 02:57
0

At the bytecode level, there is only one static initializer method per class, named <clinit>. All of the static initializers at the Java level will be compiled into a single method. (This includes both static{} blocks and any static field initialized with a non constant expression, i.e. static Foo foo = bar())

As far as how the JVM differentiates between the variables, it doesn't. Bytecode operates at a lower level of abstraction than Java source code. There are no local variables, only a stack and a table of slots that can hold values. The JVM doesn't need to know which values is which to execute your code, it just does what the bytecode says.

The only time it becomes relevant is if you want the metadata for debugging, reflection, or the like. By default, the compiler will include metadata that says which local variable each slot in the bytecode corresponds too, if any. In a case like this, each slot is used by multiple local variables, so it stores the range of the bytecode during which the slot holds the value for each source level local variable.

Anyway, this is all for static initializers. Non static initializers don't exist at the bytecode level at all. Instead, all of the initializers are concatenated and inlined into every constructor (<init> method of the class) that calls a superconstructor. This means that the code may appear more than once.

Antimony
  • 37,781
  • 10
  • 100
  • 107