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
- Can be one of either:
*aload
: Used to load a value type from an array- Can be one of either:
aload
,aaload
- Can be one of either:
invokespecial
: Invoke an instance method
A link to the full list of JVM instructions can be found here.
Example Disassembly
Simple Example
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:
iload1
- Load the firstint
argument onto the operand stackiload2
- Load the secondint
argument onto the operand stackiadd
- Add the twoint
values on the operand stackireturn
- Return theint
value on the operand stack
More Advanced Example
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:
iload1
- Push anint
constant onto the operand stack, from the local variable array at index1
istore_2
- Store anint
variable into the local variable array at index2
aload_1
- Load reference from the local variable array at index1
onto the operand stackastore_3
- Store reference into the local variable array at index3
aload_3
- Load reference from the local variable array at index3
onto the operand stackarraylength
- Get the length of the array on the operand stackistore
- Store anint
variable into the local variable array at indexn
iconst_0
- Push anint
constant onto the operand stack (int sum = 0
)istore
- Store anint
variable into the local variable array at indexn
iload
- Load anint
variable from the local variable array at indexn
onto the operand stackiload
- Load anint
variable from the local variable array at indexn
onto the operand stackif_icmpge
- Compare twoint
values on the operand stack and branch if the first is greater than or equal to the secondaload
- Load reference from the local variable arrayn
onto the operand stackiload
- Load anint
variable from the local variable array at indexn
onto the operand stackiaload
- Load anint
value from an arrayistore
- Store anint
variable into the local variable array at indexn
aload_0
- Load reference from the local variable arrayn
onto the operand stackiload_2
- Load anint
variable from the local variable array at indexn
onto the operand stackiload
- Load anint
variable from the local variable array at indexn
onto the operand stackinvokevirtual
- Invoke an instance methodistore_2
- Store anint
variable into the local variable array at indexn
iinc
- Increment anint
variable by a constantgoto
- Branch to an instruction at a given offsetiload_2
- Load anint
variable from the local variable array at indexn
onto the operand stackireturn
- Return anint
value from a method