7

Analyzing the bytecode of this simple class, I have come to the conclusion that the compiler doesn't retain any information about a local variable being final. This seems weird though, since I believe the HotSpot compiler could actually use this information to do optimizations.

Code:

public static void main(String[] args)
{
    final int i = 10;
    System.out.println(i);
}

Bytecode:

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
  stack=2, locals=2, args_size=1
     0: bipush        10
     2: istore_1      
     3: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
     6: bipush        10
     8: invokevirtual #22                 // Method java/io/PrintStream.println:(I)V
    11: return        
  LineNumberTable:
    line 7: 0
    line 8: 3
    line 9: 11
  LocalVariableTable:
    Start  Length  Slot  Name   Signature
        0      12     0  args   [Ljava/lang/String;
        3       9     1     i   I

Is there any specific reason not to retain the access flags of a local variable, other than saving disk space? Because to me, it seems that being final is a relatively non-trivial property of a variable.

Clashsoft
  • 11,553
  • 5
  • 40
  • 79
  • 4
    hotspot JITs transform the bytecode into an SSA representation internally, which AIUI makes finalness redundant anyway. And there are other fictions in java-land that are not real on the bytecode level, e.g. generics – the8472 May 10 '15 at 12:21
  • It makes sense for generics, since they would have broken downward compatibility, but `final` has been there from the very beginning. – Clashsoft May 10 '15 at 12:23
  • checked exceptions would be another example. – the8472 May 10 '15 at 12:25
  • Also true, but both Generics and Checked Exceptions are retained in the bytecode (but not used by the JVM). But since the Java compiler doesn't know about local variables or implementation in general when using binaries, it seems that it doesn't need to know about variables being final. And since, as you said, the JIT compiler doesn't care either, the language designers probably thought they won't need to store this information in the bytecode. Unfortunately, people like me who allow their compilers to inline implementations now have trouble with these formerly `final` variables. – Clashsoft May 10 '15 at 12:29
  • 3
    `final` is a keyword enforced by the _compiler_; it has no influence on the generated bytecode – fge May 10 '15 at 12:30
  • @Clashsoft - why does your compiler have a problem with final variables? Are you transforming the code in a way that does not preserve behavior? – kdgregory May 10 '15 at 12:33
  • As another example, the bytecode format allows a class to be defined with no constructors, but Java source code will force the generation of an implicit default constructor. So don't assume that Java and bytecode support the same features. – Nayuki May 10 '15 at 12:36

1 Answers1

9

The final modifier is not present in the bytecode but the compiler already uses this information to make some optimization. Although your example doesn't show it, the compiler may inline the final variable's value in the bytecode representation of the method, leading to a better performance. Something like the below can show the difference:

public int addFinal() {
    final int i = 10;
    final int j = 10;
    return i + j;
}

public int addNonFinal() {
    int i = 10;
    int j = 10;
    return i + j;
}

The generated bytecode are respectively for each method:

// addFinal
bipush 10
istore_1
bipush 10
istore_2
bipush 20
ireturn


// addNonFinal
bipush 10
istore_1
bipush 10
istore_2
iload_1
iload_2
iadd
ireturn
M A
  • 71,713
  • 13
  • 134
  • 174