Wednesday, May 25, 2011

Static final variable optimization in Java

You have a class A:
public class A {

  public static void main(String[] args) {
    System.out.println(B.finalX);
    System.out.println(B.alterableX);
  }
}
and a class B:
public class B {

  public static final String finalX = "initial final";

  public static String alterableX = "initial alterable";
}
You compile and run class A (class B is automatically compiled in case not already compiled) to get the following output:
>    initial final
>    initial alterable
You change the class B:
public class B {

  public static final String finalX = "changed final";

  public static String alterableX = "changed alterable";
}
Since only B is changed, you compile class B and run A. And you are in for a surprise because you get the following output:
>    initial final
>    changed alterable
The non-final variable is changed but the final one is not, which means, a static final variable--or a constant as it is rightly termed--of class A, is embedded in the bytecode of class B. Any changes in the constant are not incorporated until class B is recompiled.

This can be a potentially difficult-to-find error. I had this in one of my applications where I use maven to build a war. I did not do a clean build, and A got compiled before B in the build order. This resulted in A taking a value from the previous version of B, even though B was recompiled again.

Since B was the only class changed, I concentrated on B. I decompiled and ensured that the correct file went in. Suspecting changes in A, I decompiled class A too, and found that it also had the changed. It was purely on the basis of observations that I concluded on the following maxim, which I now find to be true from the above experiment:
"The compiler optimizes static final fields by embedding their values in the bytecode instead of computing them during class loading."

No comments:

Post a Comment