Skip to main content

If you haven't already, it's worth taking a look at Understanding the JVM.

Be Comfortable Taking a Byte

Being able to deduce which implementations will result in smaller, or more performant byte-code is a great skill to have.

Byte-Code

Byte-code is an intermediate language that is compiled from Java source code. It is then interpreted by the JVM to run a program.

The byte-code generated can differ from system to system, but all JVMs will be able to interpret and run the set of instructions. This is what gives Java its ability to run on so many platforms.

Common Byte-Code Operations

  • *load: Used to load a value type from a local variable onto the operand stack
    • Can be one of either: iload, fload, aload, lload, dload
  • *aload: Used to load a value type from an array
    • Can be one of either: aload, aaload
  • invokespecial: Invoke an instance method

A link to the full list of JVM instructions can be found here.

Example Disassembly

Simple Example

/benchmark/string/TestMe.java
public class TestMe {

public int processSum(int a, int b) {
return a + b;
}
}
javap -c -p TestMe.class

What are these flags?

  • -c is used to disassemble the Java class. If you want to see the actual bytecode, use this flag.
  • -p is used to expose the private members of the class.
public class com.github.chrismphilp.benchmark.string.TestMe {
public com.github.chrismphilp.benchmark.string.TestMe();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return

public int processSum(int, int);
Code:
0: iload_1
1: iload_2
2: iadd
3: ireturn
}

Therefore, for the processSum method we can infer the following operations occur in order:

  1. iload1 - Load the first int argument onto the operand stack
  2. iload2 - Load the second int argument onto the operand stack
  3. iadd - Add the two int values on the operand stack
  4. ireturn - Return the int value on the operand stack

More Advanced Example

/benchmark/string/TestMe.java
public int processSum(int[] values) {
int sum = 0;

for (int val : values) {
sum = processSum(sum, val);
}
return sum;
}

private int processSum(int a, int b) {
return a + b;
}
javap -c -p TestMe.class
public class com.github.chrismphilp.benchmark.string.TestMe {
public com.github.chrismphilp.benchmark.string.TestMe();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return

public int processSum(int[]);
Code:
0: iconst_0
1: istore_2
2: aload_1
3: astore_3
4: aload_3
5: arraylength
6: istore 4
8: iconst_0
9: istore 5
11: iload 5
13: iload 4
15: if_icmpge 38
18: aload_3
19: iload 5
21: iaload
22: istore 6
24: aload_0
25: iload_2
26: iload 6
28: invokevirtual #7 // Method processSum:(II)I
31: istore_2
32: iinc 5, 1
35: goto 11
38: iload_2
39: ireturn
}

Therefore, for the processSum method we can infer the following operations occur in order:

  1. iload1 - Push an int constant onto the operand stack, from the local variable array at index 1
  2. istore_2 - Store an int variable into the local variable array at index 2
  3. aload_1 - Load reference from the local variable array at index 1 onto the operand stack
  4. astore_3 - Store reference into the local variable array at index 3
  5. aload_3 - Load reference from the local variable array at index 3 onto the operand stack
  6. arraylength - Get the length of the array on the operand stack
  7. istore - Store an int variable into the local variable array at index n
  8. iconst_0 - Push an int constant onto the operand stack (int sum = 0)
  9. istore - Store an int variable into the local variable array at index n
  10. iload - Load an int variable from the local variable array at index n onto the operand stack
  11. iload - Load an int variable from the local variable array at index n onto the operand stack
  12. if_icmpge - Compare two int values on the operand stack and branch if the first is greater than or equal to the second
  13. aload - Load reference from the local variable array n onto the operand stack
  14. iload - Load an int variable from the local variable array at index n onto the operand stack
  15. iaload - Load an int value from an array
  16. istore - Store an int variable into the local variable array at index n
  17. aload_0 - Load reference from the local variable array n onto the operand stack
  18. iload_2 - Load an int variable from the local variable array at index n onto the operand stack
  19. iload - Load an int variable from the local variable array at index n onto the operand stack
  20. invokevirtual - Invoke an instance method
  21. istore_2 - Store an int variable into the local variable array at index n
  22. iinc - Increment an int variable by a constant
  23. goto - Branch to an instruction at a given offset
  24. iload_2 - Load an int variable from the local variable array at index n onto the operand stack
  25. ireturn - Return an int value from a method