The Static Final Inline Trap

Last week I was chasing a very interesting problem which we could only resolve with the help of a colleague. One of our testers found a bug in the current game that we are developing. If the user doesn't have Flashplayer installed, the No-Flash image is not shown properly. All code related to this, is pulled in from shared libraries which our project depends on. In the screen shot you will see something that looks like magic. There is a method which populates and returns a Map. Amongst others, one key called noflash_image_url is defining the location of the No-Flash image. In the debugger pane to the lower right, you can see the static final constant NOFLASH_IMAGE_URL evaluating to the correct value. This value is the correct location of the No-Flash image.

The strange thing however, is that the Map contains another (wrong) value, even though the Map value is also set using the same static final constant NOFLASH_IMAGE_URL. You can see that in the lower middle pane of the screen shot. So for some strange reason, the constant is evaluating to both the correct and the wrong value. I guess we could call it semi-constant in this particular case.

Anytime you see something weird like this, your first thought should be class loading problem. Based on my experience, the weirdest problems are often rooted in class loading. However this problem is of a different kind. For a better understanding, you have to know that the Class which initializes the Map is a dependency that ours project pulls in from a Maven dependency to library A. This library A then depends on another (transient) Maven library B which contains the NOFLASH_IMAGE_URL constant. Our project also defines a direct Maven dependency to library B. This is needed because library B changes quite often and we always want the latest version of B in our project. The latest version of B does contain the correct value the No-Flash image in the NOFLASH_IMAGE_URL constant. So one might think, that the Maven dependency resolution mechanism pulled in an old version of library B, but this wasn't the case.

A colleague then hinted me in the right direction. The Java compiler often does something called inlining for access to a static final variable. This is an optimization as the value of a final variable cannot change after it has been assigned. To verify this we ran the Java class file disassembler (javap) over the class file in library A.

   78: pop
   79: aload_2
   80: ldc #108; //String noflash_image_url
   82: ldc #109; //String
   84: invokeinterface #100,  3; //InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

As you can see, the compiler is really inlining the value in places where we, by running a debugger over the source code, expect an evaluation of the static final variable at runtime. This is definitely something you have to be aware of. We can fix this, by re-compiling library A against a newer version of library B, which will inline the correct No-Flash image. Another option to prevent inlining would be to declare the constant like this:

  public static final String NOFLASH_IMAGE_URL = new String ("...");

But then this might be really hard to understand for other developers and they might revert this change to a String literal if not properly documented. Update: I just realized that this problem is featured as Puzzle 93: Class Warfare in the Java Puzzlers book from Joshua Bloch. The compiler will inline all constant variables, such as Primitives and Strings which are initialized with a constant expression. Surprisingly, null is not inlined, neither are Java 5 enums.