Showing posts with label mustang. Show all posts
Showing posts with label mustang. Show all posts

Monday, July 09, 2007

Java Puzzler

I've been reading Java Puzzlers by Josh Bloch and Neal Gafter. I usually read (and hopefully solve) one puzzle each day. I had one last week that was ridiculously hard. It was puzzle #44 Cutting Class. The puzzle is pretty simple. You have a simple class called Missing. Then you have two other classes, Strange1 and Strange2. Both have a main method where they construct a new instance of Missing using the usual

Missing m = new Missing();

Both classes also have a try ... catch block where they catch a NoClassDefFoundError. The difference in the two classes is that the first (Strange1) wraps all references to Missing inside the try, but the other(Strange2) does a blank initialization (Missing m;) outside, and then invokes the constructor inside (m = new Missing()) the try.

In the puzzle, you compile all three class and then delete Missing.class. You then execute Strange1 and Strange2. The puzzle is that Strange1 throws a NoClassDefFoundError, but Strange2 does not.

In the solution, the authors point out that the behavior is undefined, so it is possible for different JVMs to exhibit different behavior. The book was written with Java 1.5 in mind. I tried a couple of different 1.5 JVMs (HotSpot and IBM's) and both behaved just as the book claimed.

In the solution to the problem, you have to use a little known part of the JDK, the javap tool. I had not used javap in a long, long time. I used it once to track down a problem that turned out to be a classloader issue. Anyways, javap shows you the instructions that the JVM is going to execute. The key to the puzzler is that in Strange1, the memory location allocated for the instance of Missing has to get merged with the instance of NoClassDefFoundError caught in the catch block. This causes the JVM to have to computer the most specific common superclass of the two classes, and thus load Missing. This causes a NoClassDefFoundError to be thrown before any execution can take place. In Strange2, the memory location is allocated outside the try ... catch, so it is different location than the location for the caught NoClassDefFoundError, so no merging, etc.

For kicks, I then tried a couple of 1.6 JVMs( HotSpot and JRockit.) With a 1.6 JVM, neither Strange1 nor Strange2 throw a NoClassDefFoundError! I did a javap on the 1.6 class files, and they were identical to the 1.5 class files. So what gives?

The key is JSR 202. This JSR introduced a new class verifier (the split verifier) in Java 1.6. One of the ideas on this JSR was to improve class loading time by speeding up class verification. Class verification is where the exception gets thrown in the puzzler. So how does the new class verifier avoid the exception? Basically it allows for extra metadata to be included in the class file around join points. The merging of the Missing instance and the NoClassDefFoundError instance is a join point. So with Java 1.6, the Missing class does not have to be loaded to compute the most specific superclass of the type merged variables because it is already embedded in the class file. So the Missing instance is not attempted to be loaded until its constructor is invoked during execution, and at that point the error is caught. Beautiful.

Monday, November 27, 2006

JRockit and Tomcat

I decided to try out BEA's JRockit JVM a try as my server JVM. It bills itself as being a much faster JVM on x86 machines. So far it doesn't seem that much faster, except when running the JVM in debug mode. For that, it seems to launch much faster than HotSpot did. Actually there seems to be little difference in its startup time in debug mode vs. "normal" non-debug mode.

The only weird thing was that when I attached Eclipse to it, Eclipse complained about some line numbers not being reported by the code. This had no effect on any of the breakpoints I had set. Those worked flawlessly. It seemed to be around CGLIB enhanced byte-code, but there were obviously no breakpoints set in such code. I set a breakpoint in code that I knew would be CGLIB enhanced, but that still worked fine.

Just for kicks, I decided to also use JDK 1.6. I had a colleague tell me that "Mustang" was a lot faster than JDK 1.5. I was in for quite a surprise. My application threw an NPE while starting up. Why would changing JVMs cause an NPE? The startup was indeed much faster, but that was because the NPE caused less things to load. The NPE happened while Spring was "gluing" together several beans. I investigated a little, but found nothing obvious. Maybe I will dig deeper later.

Update: Not sure what caused the above problem, but since the official release of Mustang, I've switched over to using very successfully. There are definitely some big performance improvements, both in the client and server modes. I'm looking forward to making use of its scripting language features.