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?
-cis used to disassemble the Java class. If you want to see the actual bytecode, use this flag.-pis 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 firstintargument onto the operand stackiload2- Load the secondintargument onto the operand stackiadd- Add the twointvalues on the operand stackireturn- Return theintvalue 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 anintconstant onto the operand stack, from the local variable array at index1istore_2- Store anintvariable into the local variable array at index2aload_1- Load reference from the local variable array at index1onto the operand stackastore_3- Store reference into the local variable array at index3aload_3- Load reference from the local variable array at index3onto the operand stackarraylength- Get the length of the array on the operand stackistore- Store anintvariable into the local variable array at indexniconst_0- Push anintconstant onto the operand stack (int sum = 0)istore- Store anintvariable into the local variable array at indexniload- Load anintvariable from the local variable array at indexnonto the operand stackiload- Load anintvariable from the local variable array at indexnonto the operand stackif_icmpge- Compare twointvalues on the operand stack and branch if the first is greater than or equal to the secondaload- Load reference from the local variable arraynonto the operand stackiload- Load anintvariable from the local variable array at indexnonto the operand stackiaload- Load anintvalue from an arrayistore- Store anintvariable into the local variable array at indexnaload_0- Load reference from the local variable arraynonto the operand stackiload_2- Load anintvariable from the local variable array at indexnonto the operand stackiload- Load anintvariable from the local variable array at indexnonto the operand stackinvokevirtual- Invoke an instance methodistore_2- Store anintvariable into the local variable array at indexniinc- Increment anintvariable by a constantgoto- Branch to an instruction at a given offsetiload_2- Load anintvariable from the local variable array at indexnonto the operand stackireturn- Return anintvalue from a method