Using javap
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.