Stack
The stack is a very simple data structure, imagine it as a stack of pancakes.
A stack is essentially an array of data with 2 main operations. Adding an element to the stack is called pushing and removing an element from the stack is called popping.
A stack is controlled via a stack pointer, a number that keeps track of the current index we are at in the stack.
In the JVM stack specifically the stack element pushed first will be popped last assuming the size of the stack is >1, otherwise the first element pushed will be the element popped next because stack elements are consumed in reverse.
Stack activity
These are the main instructions not including field and method reference ones that commonly are used to manipulate, consume or produce stack elements.
bipush
Push an 8-bit (1 byte) in size signed integer onto the stack.
-127 to 128
sipush
Push a 16-bit (2 byte) in size signed integer onto the stack.
-32,768 to 32,767
pop
Pop an element off the stack.
N/A
pop2
Pop 2 elements off the stack.
N/A
dup
Duplicate an element on the stack.
N/A
*add
Add the last two numeric elements from the stack together and push the result.
N/A
*sub
Subtract from the last numeric element on the stack using the second last one and push the result.
N/A
*div
Divide the last numeric element on the stack into the value of the second last one and push the result.
N/A
*mul
Multiply two numeric elements from the stack together and push the result.
N/A
*store
Store the last stack element into a local variable with the index as the operand.
N/A
*load
Load the value of a local variable onto the stack with the index as the operand.
N/A
ldc
Load a constant from the constant pool onto the stack using it's index as the operand.
Varies on the entry
*const_*
Load an immediate value onto the stack using it's respective opcode.
N/A
Instructions like invokestatic, invokevirtual, invokeinterface and invokespecial consume and produce elements based on the signature of the method they are invoking.
If the method being invoked has a return type that is not void then the return value is pushed to the stack after the invocation has been completed, otherwise nothing happens.
For each parameter of the method being invoked a stack element is popped and used as an invocation argument.
Meaning in-order to invoke a static method with the signature: int test(int a, int b);
The stack must look like this (pA and pB being the stack elements used for the invocation arguments): [pB, pA]
The stack is popped in reverse which is why invocation arguments must be supplied in reverse order otherwise it can cause undefined behavior. This also goes for every stack consumption operation that consumes >1 stack element.
Wide instructions
I will describe ldc_w and goto_w to find the rest you can look at the JVM specification.
wide LDC's
The JVM also has a few versions of instructions suffixed by "_w" like ldc_w. These instructions are called wide, in the case of ldc_w 1 extra byte is given for the entry index (standard ldc only gives 1 byte for the index).
The index is then constructed as (idx_byte1 << 8 | idx_byte2) assuming the ldc is wide.
wide GOTO's
Just like there is a wide version of ldc there is also a wide version of the goto instruction which works the same way only that the standard goto by default gives 2 bytes for the relative address constructed as (bb1 and bb2 being branchbyte1 and branchbyte2) (bb1 << 8 | bb2).
The wide goto gives 4 bytes for the relative address constructed as (bb1, bb2, bb3 and bb4 being branchbyte*):
Last updated