Programming Header

Using javap

Back Button   Return to the Java Programming Corner.


When compiling programs in Java, it is well known that the code is not compiled to machine code. Programs in Java are compiled to into an intermediate bytecode format that is executed by a Java Virtual Machine. Most developers although have never seen byte code. (nor have many ever wanted to see it!) One way to view the byte code is to compile your class and then open the .class file in a hex editor and translate the bytecodes by referring to the virtual machine specification. A much easier way is to utilize the command-line utility javap. The Java SDK from Sun includes the javap disassembler, that will convert the byte codes into human-readable mnemonics.

public class JavapDemoApplication {
    public static void main(String[] args) {
        System.out.println("javap Demo Application");
    }
}
Using the '-c' option, you can get a bytecode listing from javap as follows:


% javac JavapDemoApplication.java
% javap -c JavapDemoApplication

Compiled from JavapDemoApplication.java
public class JavapDemoApplication extends java.lang.Object {
    public JavapDemoApplication();
    public static void main(java.lang.String[]);
}

Method JavapDemoApplication()
   0 aload_0
   1 invokespecial #1 <Method java.lang.Object()>
   4 return

Method void main(java.lang.String[])
   0 getstatic #2 <Field java.io.PrintStream out>
   3 ldc #3 <String "javap Demo Application">
   5 invokevirtual #4 <Method void println(java.lang.String)>
   8 return


To gain a better understanding of byte code, lets start with the first instruction in the main method:

  0 getstatic #2 <Field java.io.PrintStream out>
The initial integer is the offset of the instruction in the method. So the first instruction begins with a '0'. The mnemonic for the instruction follows the offset. In this example, the 'getstatic' instruction pushes a static field onto a data structure called the operand stack. Later instructions can reference the field in this data structure. Following the getstatic instruction is the field to be pushed. In this case the field to be pushed is "#2 <Field java.io.PrintStream out>." If you examined the bytecode directly, you would see that the field information is not embedded directly in the instruction. Instead, like all constants used by a Java class, the field information is stored in a shared pool. Storing field information in a constant pool reduces the size of the bytecode instructions. This is because the instructions only have to store the integer index into the constant pool instead of the entire constant. In this example, the field information is at location #2 in the constant pool. The order of items in the constant pool is compiler dependent, so you might see a number other than '#2.'

After analyzing the first instruction, it's easy to guess the meaning of the other instructions. The 'ldc' (load constant) instruction pushes the constant "javap Demo Application" onto the operand stack. The 'invokevirtual' invokes the println method, which pops its two arguments from the operand stack. Don't forget that an instance method such as println has two arguments: the obvious string argument, plus the implicit 'this' reference.