javap Usage Unfolds: What’s Hidden Inside Your Java Class Files?
What’s javap, how do you use it and when would you want to disassemble class files?
There are a number of tools available to us as part of the Java Development Kit (JDK), that help gain a better understanding of Java code. One of those tools is the javap command, that gives us backstage passes to Java class files.
In the following post, we’ll take a look into javap, understand how it can help us and see exactly how we can use it. Code, disassemble!
What is javap?
javap is a command line tool that disassembles Java class files: it takes apart our class files, and reveals what’s inside. The tool translates a binary format class file to human readable code. Well, for some humans.
There are a number of outputs javap offers, which vary according to the data we’re interested in. By default, javap prints declarations of non-private members of each of the classes.
As for what the p in javap stands for, all evidence points to “prints”, since the javap command prints out the bytecode within the class.
A nice way in which we can use javap, is to explore exceptions. If you want to level up your knowledge and see what happens when an exception is thrown, check out our post about the surprising truth of Java exceptions.
Using javap with our classes
Now that we know what javap is, it’s time to experiment using it on our code. We do that by typing the command, choosing an option and adding the class name:
javap [options] classname
As we pointed out, the options, which we can also call flags, will determine what our output will be. We can choose from a number of flags, that include:
- -l – Prints out line and local variable tables
- -public – Shows only public classes and members
- -protected – Shows only protected and public classes and members
- -package – Shows only package, protected, and public classes and members
- -p – Shows all classes and members
- -Jflag – Pass flag directly to the runtime system
- -s – Prints internal type signatures
- -sysinfo – Shows system information (path, size, date, MD5 hash) of the class being processed
- -constants – Shows static final constants
- -c – Prints out disassembled code
- -verbose – Prints stack size, number of locals and args for methods
Diving into the bytecode with javap
After we’ve listed what we can do with javap, it’s time to figure how it actually works. In order to do so, we’ve create a basic class called ExampleClass:
public class ExampleClass { private int a = 0; private int b = 0; public static void main(String[] args) { System.out.println("Hello world!"); } }
Now let’s take a deeper look at it with the help of javap. First, we’ll use the javap command without any other flags, to print out non-private members. Our output is this:
$ javap ExampleClass Compiled from "ExampleClass.java" public class ExampleClass { public ExampleClass(); public static void main(java.lang.String[]); }
As you can see, this is a pretty “plain” view of our original code, without any information about our private integers and logic. That’s a good start, but what if we want to look even deeper? Let’s try using -c, to print out the disassembled code:
$ javap -c ExampleClass Compiled from "ExampleClass.java" public class ExampleClass { public ExampleClass(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: iconst_0 6: putfield #2 // Field a:I 9: aload_0 10: iconst_0 11: putfield #3 // Field b:I 14: return public static void main(java.lang.String[]); Code: 0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #5 // String Hello world! 5: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return }
Now we have our bytecode in a somewhat-readable form, where we can identify our methods, integers, commands and strings. But wait, there’s more. What if we want more information about this class? That’s where our verbose flag can help us out:
$ javap -v ExampleClass Classfile /Users/es/ExampleClass.class Last modified May 22, 2017; size 496 bytes MD5 checksum 7d29362228a3128e67b0c20c8bb54ee1 Compiled from "ExampleClass.java" public class ExampleClass SourceFile: "ExampleClass.java" minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #8.#20 // java/lang/Object."<init>":()V #2 = Fieldref #7.#21 // ExampleClass.a:I #3 = Fieldref #7.#22 // ExampleClass.b:I #4 = Fieldref #23.#24 // java/lang/System.out:Ljava/io/PrintStream; #5 = String #25 // Hello world! #6 = Methodref #26.#27 // java/io/PrintStream.println:(Ljava/lang/String;)V #7 = Class #28 // ExampleClass #8 = Class #29 // java/lang/Object #9 = Utf8 a #10 = Utf8 I #11 = Utf8 b #12 = Utf8 <init> #13 = Utf8 ()V #14 = Utf8 Code #15 = Utf8 LineNumberTable #16 = Utf8 main #17 = Utf8 ([Ljava/lang/String;)V #18 = Utf8 SourceFile #19 = Utf8 ExampleClass.java #20 = NameAndType #12:#13 // "<init>":()V #21 = NameAndType #9:#10 // a:I #22 = NameAndType #11:#10 // b:I #23 = Class #30 // java/lang/System #24 = NameAndType #31:#32 // out:Ljava/io/PrintStream; #25 = Utf8 Hello world! #26 = Class #33 // java/io/PrintStream #27 = NameAndType #34:#35 // println:(Ljava/lang/String;)V #28 = Utf8 ExampleClass #29 = Utf8 java/lang/Object #30 = Utf8 java/lang/System #31 = Utf8 out #32 = Utf8 Ljava/io/PrintStream; #33 = Utf8 java/io/PrintStream #34 = Utf8 println #35 = Utf8 (Ljava/lang/String;)V { public ExampleClass(); flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: iconst_0 6: putfield #2 // Field a:I 9: aload_0 10: iconst_0 11: putfield #3 // Field b:I 14: return LineNumberTable: line 1: 0 line 3: 4 line 4: 9 public static void main(java.lang.String[]); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #5 // String Hello world! 5: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 7: 0 line 8: 8 }
A whole lot of lines unravel before us. We can see the following things:
1. Metadata – Where this class file is located, when it was last modified, and the class hash with its unique id
2. Constant pool – a table of structures that refers to the various constants within the ClassFile structure and its substructures. It holds the symbolic information of classes, interfaces, class instances, field names, string constants or arrays.
3. Bytecode – an instruction set written in a language the JVM can “read”
We at OverOps use javap as a research tool, to dig inside classes. We’ve used it recently to discover how IBM J9 works, to offer support for developers who use this JVM. If you want to learn more about how we help teams cut down debugging time and know where, when and why code breaks in production – click here to schedule a demo.
Now that we know how we can use javap, it’s time to answer an important question:
What is it good for?
Javap is cool, especially if you’re a data fan like us and want to know what’s going on behind the code. But beside the nice code-exploration adventure, it’s also pretty useful.
It could come in handy for those cases in which the original source code isn’t available, showing what methods are available for your use. It could also help find out what’s inside someone else’s code, or a 3rd party class.
We can also use javap as a class verifier on Java classes, making sure each loaded class file is structured in the appropriate way.
At first, javap feels like a magic wand that gives us every piece of information within the code, but it’s not a complete solution to every problem we’re facing. It might help “reverse-engineer” some of the code, but it’s more of a clue in an elaborate puzzle.
If you’re looking for a way to turn javap bytecode output to functional Java code, and not just a “data-list”, you’d need the help of decompiling tools such as JD, Mocha, and others.
Final Thoughts
While you won’t use it often, javap is a useful tool to keep in mind. Other than the fact that it’s a cool “trick”, it offers a deeper dive into Java code, showing us what’s going on behind the scenes and how the JVM consumes our code.
Reference: | javap Usage Unfolds: What’s Hidden Inside Your Java Class Files? from our JCG partner Henn Idan at the OverOps blog. |
Very helpful article. Thank you, I found a lot of interesting things here )