google-site-verification: google61fe8ba583a51912.html
Javap Tool for Disassembling Classes in Java

Introduction to Javap

javap is the Java Class File Disassembler included with the JDK. It disassembles compiled Java class files and shows the structure, bytecode, and metadata of classes in a human-readable format.

What Javap Shows

  • Class declaration and inheritance
  • Fields and methods with signatures
  • Bytecode instructions
  • Constant pool information
  • Access modifiers and annotations

1. Basic Javap Usage

Command Line Syntax

# Basic disassembly javap className # With full verbose output javap -v className # Disassemble with specific options javap -c -p -s className

Simple Example Class

// Simple class for disassembly demonstration public class SimpleCalculator { private int value; public SimpleCalculator() { this.value = 0; } public SimpleCalculator(int initialValue) { this.value = initialValue; } public int add(int number) { return this.value + number; } public static int multiply(int a, int b) { return a * b; } private void reset() { this.value = 0; } }

Basic Disassembly Output

# Compile first javac SimpleCalculator.java # Basic disassembly javap SimpleCalculator

Output:

Compiled from "SimpleCalculator.java" public class SimpleCalculator { private int value; public SimpleCalculator(); public SimpleCalculator(int); public int add(int); public static int multiply(int, int); private void reset(); }

2. Common Javap Options

Complete Option Reference

OptionDescription
-helpPrint help message
-versionVersion information
-v or -verboseVerbose output including bytecode
-lPrint line and local variable tables
-publicShow only public classes and members
-protectedShow protected/public classes and members
-packageShow package/protected/public members (default)
-p or -privateShow all classes and members
-cDisassemble bytecode
-sPrint internal type signatures
-sysinfoShow system info (path, size, date, MD5 hash)
-constantsShow static final constants

3. Detailed Bytecode Analysis

Verbose Disassembly Example

javap -v -p SimpleCalculator

Partial Output:

Classfile /path/to/SimpleCalculator.class Last modified Dec 1, 2024; size 487 bytes MD5 checksum a1b2c3d4e5f6... Compiled from "SimpleCalculator.java" public class SimpleCalculator minor version: 0 major version: 55 flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #5 // SimpleCalculator super_class: #6 // java/lang/Object interfaces: 0, fields: 1, methods: 4, attributes: 1 // Constant Pool (abbreviated) Constant pool: #1 = Methodref #6.#21 // java/lang/Object."<init>":()V #2 = Fieldref #5.#22 // SimpleCalculator.value:I #3 = Methodref #5.#23 // SimpleCalculator."<init>":(I)V // ... more constants // Fields private int value; descriptor: I flags: (0x0002) ACC_PRIVATE // Constructors and Methods public SimpleCalculator(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=1, 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 value:I 9: return public int add(int); descriptor: (I)I flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 0: aload_0 1: getfield #2 // Field value:I 4: iload_1 5: iadd 6: ireturn

Bytecode Instruction Analysis

public class BytecodeExample { private String name; private int count; public BytecodeExample(String name) { this.name = name; this.count = 0; } public void increment() { count++; } public String process(int multiplier) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < multiplier; i++) { sb.append(name).append("-").append(count); } return sb.toString(); } }

Disassemble with bytecode:

javap -c -p BytecodeExample

Output:

public class BytecodeExample { private java.lang.String name; private int count; public BytecodeExample(java.lang.String); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: aload_1 6: putfield #2 // Field name:Ljava/lang/String; 9: aload_0 10: iconst_0 11: putfield #3 // Field count:I 14: return public void increment(); Code: 0: aload_0 1: dup 2: getfield #3 // Field count:I 5: iconst_1 6: iadd 7: putfield #3 // Field count:I 10: return public java.lang.String process(int); Code: 0: new #4 // class java/lang/StringBuilder 3: dup 4: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V 7: astore_2 8: iconst_0 9: istore_3 10: iload_3 11: iload_1 12: if_icmpge 49 15: aload_2 16: aload_0 17: getfield #2 // Field name:Ljava/lang/String; 20: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 23: ldc #7 // String - 25: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28: aload_0 29: getfield #3 // Field count:I 32: invokevirtual #8 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 35: pop 36: iinc 3, 1 39: goto 10 42: astore 4 44: aload_2 45: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 48: areturn 49: aload_2 50: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 53: areturn }

4. Advanced Class Analysis

Analyzing Inheritance and Interfaces

// Complex class with inheritance and interfaces public interface Shape { double area(); double perimeter(); } public abstract class AbstractShape implements Shape { protected String color; public AbstractShape(String color) { this.color = color; } public String getColor() { return color; } } public class Circle extends AbstractShape { private double radius; public Circle(String color, double radius) { super(color); this.radius = radius; } @Override public double area() { return Math.PI * radius * radius; } @Override public double perimeter() { return 2 * Math.PI * radius; } public final double getRadius() { return radius; } }

Analyze the Circle class:

javap -v -p Circle

Key Output Sections:

// Class hierarchy public class Circle extends AbstractShape flags: (0x0021) ACC_PUBLIC, ACC_SUPER // Interfaces implemented Interfaces: #0 = Shape // Method overrides and implementations public double area(); descriptor: ()D flags: (0x0001) ACC_PUBLIC Code: stack=6, locals=1, args_size=1 0: ldc2_w #2 // double 3.141592653589793d 3: dload_0 4: getfield #4 // Field radius:D 7: dmul 8: dload_0 9: getfield #4 // Field radius:D 12: dmul 13: dreturn public final double getRadius(); descriptor: ()D flags: (0x0011) ACC_PUBLIC, ACC_FINAL

Analyzing Annotations

import java.lang.annotation.*; import javax.validation.constraints.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface Author { String name(); String date(); } @Author(name = "John Doe", date = "2024-12-01") public class AnnotatedClass { @NotNull private String title; @Min(1) @Max(100) private int priority; @Deprecated public void oldMethod() {} public void newMethod(@NotNull String param) {} }

Analyze annotations:

javap -v AnnotatedClass

Annotations Section:

// Class annotations RuntimeVisibleAnnotations: 0: #30(#31=s#32,#33=s#34) Author( name="John Doe" date="2024-12-01" ) // Field annotations private java.lang.String title; descriptor: Ljava/lang/String; flags: (0x0002) ACC_PRIVATE RuntimeVisibleAnnotations: 0: #35() javax.validation.constraints.NotNull // Method annotations public void oldMethod(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Deprecated: true public void newMethod(java.lang.String); descriptor: (Ljava/lang/String;)V flags: (0x0001) ACC_PUBLIC RuntimeVisibleParameterAnnotations: parameter 0: 0: #35() javax.validation.constraints.NotNull

5. Practical Use Cases and Examples

Debugging and Understanding Code

public class DebugExample { private static final int MAX_SIZE = 100; private int[] data; private int size; public DebugExample() { this.data = new int[MAX_SIZE]; this.size = 0; } public void add(int value) { if (size >= MAX_SIZE) { throw new IllegalStateException("Array full"); } data[size++] = value; } public int find(int value) { for (int i = 0; i < size; i++) { if (data[i] == value) { return i; } } return -1; } public void process() { int sum = 0; for (int i = 0; i < size; i++) { sum += data[i] * 2; } System.out.println("Sum: " + sum); } }

Analyze for optimization:

javap -c -v DebugExample

Understanding Compiler Optimizations

public class OptimizationExample { // Analyze how compiler handles different constructs public int simpleLoop() { int sum = 0; for (int i = 0; i < 100; i++) { sum += i; } return sum; } public int stringConcatenation() { String result = ""; for (int i = 0; i < 10; i++) { result += i; // Inefficient concatenation } return result.length(); } public int optimizedString() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 10; i++) { sb.append(i); } return sb.toString().length(); } public final int constantFolding() { return 10 + 20 * 30; // Should be compiled to constant } }

Compare bytecode:

# Compare string concatenation vs StringBuilder javap -c OptimizationExample

Bytecode Comparison:

// Inefficient string concatenation public int stringConcatenation(); Code: 0: ldc #2 // String 2: astore_1 3: iconst_0 4: istore_2 5: iload_2 6: bipush 10 8: if_icmpge 37 11: new #3 // class java/lang/StringBuilder 14: dup 15: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 18: aload_1 19: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 22: iload_2 23: invokevirtual #6 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 26: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 29: astore_1 30: iinc 2, 1 33: goto 5 36: return // Efficient StringBuilder usage public int optimizedString(); Code: 0: new #3 // class java/lang/StringBuilder 3: dup 4: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 7: astore_1 8: iconst_0 9: istore_2 10: iload_2 11: bipush 10 13: if_icmpge 30 16: aload_1 17: iload_2 18: invokevirtual #6 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 21: pop 22: iinc 2, 1 25: goto 10 28: return

6. Advanced Javap Techniques

Analyzing Inner Classes

public class OuterClass { private String outerField = "outer"; public class InnerClass { private String innerField = "inner"; public void print() { System.out.println(outerField + " - " + innerField); } } public static class StaticNestedClass { private String staticField = "static"; public void print() { System.out.println(staticField); } } public void createLocalClass() { class LocalClass { public void print() { System.out.println("Local class"); } } new LocalClass().print(); } public Runnable createAnonymousClass() { return new Runnable() { @Override public void run() { System.out.println("Anonymous class"); } }; } }

Analyze compiled inner classes:

# Compile first javac OuterClass.java # Analyze main class javap -v OuterClass # Analyze inner classes javap -v OuterClass\$InnerClass javap -v OuterClass\$StaticNestedClass javap -v OuterClass\$1LocalClass javap -v OuterClass\$1

Inner Class Characteristics:

// Inner class has reference to outer class public class OuterClass$InnerClass flags: (0x0020) ACC_SUPER InnerClasses: #24= #2 of #23; // InnerClass=class OuterClass$InnerClass of class OuterClass // Access to outer class field aload_0 getfield #1 // Field this$0:LOuterClass; getfield #2 // Field OuterClass.outerField:Ljava/lang/String;

Analyzing Synthetic Methods

public class SyntheticExample { private String privateField; public class Inner { public void accessPrivate() { // Accessing private field of outer class // generates synthetic accessor method System.out.println(privateField); } } }

Synthetic methods in output:

// Synthetic accessor method generated by compiler static java.lang.String access$000(SyntheticExample); descriptor: (LSyntheticExample;)Ljava/lang/String; flags: (0x1008) ACC_STATIC, ACC_SYNTHETIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: getfield #1 // Field privateField:Ljava/lang/String; 4: areturn

7. Javap with Java Program

Programmatic Javap Execution

import java.io.*; import java.util.*; public class ProgrammaticJavap { /** * Execute javap command programmatically and capture output */ public static List<String> disassembleClass(String className, String... options) throws IOException, InterruptedException { List<String> command = new ArrayList<>(); command.add("javap"); // Add options if (options != null) { command.addAll(Arrays.asList(options)); } // Add class name command.add(className); ProcessBuilder pb = new ProcessBuilder(command); pb.redirectErrorStream(true); Process process = pb.start(); // Read output List<String> output = new ArrayList<>(); try (BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { output.add(line); } } int exitCode = process.waitFor(); if (exitCode != 0) { throw new IOException("javap failed with exit code: " + exitCode); } return output; } /** * Analyze multiple classes with different options */ public static void analyzeClasses(String... classNames) { for (String className : classNames) { try { System.out.println("\n=== Analyzing: " + className + " ==="); // Basic analysis List<String> basicOutput = disassembleClass(className); System.out.println("Basic structure:"); basicOutput.forEach(System.out::println); // Bytecode analysis System.out.println("\nBytecode:"); List<String> bytecodeOutput = disassembleClass(className, "-c"); bytecodeOutput.forEach(System.out::println); } catch (Exception e) { System.err.println("Failed to analyze " + className + ": " + e.getMessage()); } } } public static void main(String[] args) { // Example usage try { // Analyze current class analyzeClasses("ProgrammaticJavap"); // Analyze system classes List<String> stringOutput = disassembleClass("java.lang.String", "-c", "-p"); System.out.println("\n=== String class bytecode (first 20 lines) ==="); stringOutput.stream().limit(20).forEach(System.out::println); } catch (Exception e) { e.printStackTrace(); } } }

Custom Bytecode Analyzer

import java.util.regex.*; import java.util.*; public class BytecodeAnalyzer { /** * Parse javap output to extract method information */ public static Map<String, MethodInfo> analyzeMethods(List<String> javapOutput) { Map<String, MethodInfo> methods = new LinkedHashMap<>(); Pattern methodPattern = Pattern.compile( "^(public|private|protected|).*? (\\w+)\\(.*\\)"); Pattern bytecodePattern = Pattern.compile("^\\s+\\d+:\\s+(\\w+)"); String currentMethod = null; List<String> currentBytecode = new ArrayList<>(); for (String line : javapOutput) { Matcher methodMatcher = methodPattern.matcher(line); if (methodMatcher.find()) { // Save previous method if (currentMethod != null) { methods.put(currentMethod, new MethodInfo(currentMethod, currentBytecode)); } // Start new method currentMethod = methodMatcher.group(2); currentBytecode = new ArrayList<>(); } Matcher bytecodeMatcher = bytecodePattern.matcher(line); if (bytecodeMatcher.find() && currentMethod != null) { currentBytecode.add(bytecodeMatcher.group(1)); } } // Don't forget the last method if (currentMethod != null) { methods.put(currentMethod, new MethodInfo(currentMethod, currentBytecode)); } return methods; } public static class MethodInfo { private final String name; private final List<String> instructions; private final Map<String, Integer> instructionCount; public MethodInfo(String name, List<String> instructions) { this.name = name; this.instructions = instructions; this.instructionCount = new HashMap<>(); // Count instruction frequency for (String instruction : instructions) { instructionCount.merge(instruction, 1, Integer::sum); } } // Getters public String getName() { return name; } public List<String> getInstructions() { return instructions; } public Map<String, Integer> getInstructionCount() { return instructionCount; } public int getInstructionCount() { return instructions.size(); } } public static void printAnalysis(Map<String, MethodInfo> methods) { System.out.println("=== BYTECODE ANALYSIS ==="); System.out.printf("Total methods analyzed: %d%n%n", methods.size()); for (MethodInfo method : methods.values()) { System.out.printf("Method: %s%n", method.getName()); System.out.printf(" Total instructions: %d%n", method.getInstructionCount()); System.out.printf(" Unique instructions: %d%n", method.getInstructionCount().size()); // Show most common instructions System.out.println(" Most frequent instructions:"); method.getInstructionCount().entrySet().stream() .sorted(Map.Entry.<String, Integer>comparingByValue().reversed()) .limit(5) .forEach(entry -> System.out.printf(" %s: %d%n", entry.getKey(), entry.getValue())); System.out.println(); } } }

8. Practical Tips and Best Practices

Common Javap Patterns

# 1. Quick method signature check javap -p MyClass # 2. Full bytecode analysis for debugging javap -c -v MyClass > bytecode.txt # 3. Compare two versions of a class javap -c OldVersion.class > old.txt javap -c NewVersion.class > new.txt diff old.txt new.txt # 4. Analyze system classes javap -c java.lang.String # 5. Check for synthetic methods and bridges javap -v MyClass | grep -E "(synthetic|bridge)" # 6. Analyze annotation processing javap -v AnnotatedClass | grep -A5 -B5 "Annotation"

Understanding Common Bytecode Patterns

public class BytecodePatterns { // Field access pattern private int field; public int getField() { return field; // aload_0, getfield, ireturn } // Method invocation pattern public void methodCall() { System.out.println("Hello"); // getstatic, ldc, invokevirtual } // Loop pattern public void loop() { for (int i = 0; i < 10; i++) { // iconst_0, istore_1, iload_1, bipush 10, if_icmpge // iinc 1, 1, goto } } // Exception handling pattern public void exceptionHandling() { try { riskyOperation(); } catch (Exception e) { handleException(e); } } // String concatenation pattern (Java 9+) public String stringConcat(String a, String b) { return a + b; // invokedynamic #0, makeConcatWithConstants } }

Summary

Key Benefits of Javap:

  1. Debugging: Understand how Java code compiles to bytecode
  2. Performance Analysis: Identify inefficient bytecode patterns
  3. Learning: Learn Java internals and JVM operation
  4. Verification: Check compiler optimizations and synthetic methods
  5. Reverse Engineering: Understand third-party library behavior

Common Use Cases:

  • Performance Optimization: Identify bytecode-level inefficiencies
  • Compiler Behavior: Understand how language features compile
  • Educational Purposes: Learn JVM internals
  • Debugging: Troubleshoot mysterious behavior
  • Security Analysis: Examine potentially malicious code

Limitations:

  • Shows bytecode, not original source
  • Requires understanding of JVM instruction set
  • Verbose output can be overwhelming
  • Doesn't show runtime behavior

Javap is an essential tool for Java developers who want to understand what's happening under the hood and optimize their code at the bytecode level.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper