Let's look at a (progressively less and less) detailed description of each class of bytecodes.
For each bytecode, some brief text describes its function and a textual "picture" of the stack, both before and after the bytecode has been executed, is shown. This text picture will look like the following:
..., value1, value2 => ..., value3
This means that the bytecode expects two operands-value1 and value2-to be on the top of the stack, pops them both off the stack, operates on them to produce value3, and pushes value3 back onto the top of the stack. You should read each stack from right to left, with the rightmost value being the top of the stack. The ... is read as "the rest of the stack below," which is irrelevant to the current bytecode. All operands on the stack are 32 bits wide.
Because most bytecodes take their arguments from the stack and place their results back there, the brief text descriptions that follow only say something about the source or destination of values if they are not on the stack. For example, the description "Load integer from local variable." means that the integer is loaded onto the stack, and "Integer add." intends its integers to be taken from-and the result returned to-the stack.
Bytecodes that don't affect control flow simply move the pc
onto the next bytecode that follows in sequence. Those that do
affect the pc say so explicitly.
Whenever you see byte1, byte2,
and so forth, it refers to the first byte, second byte, and so
on, that follow the opcode byte itself. After such a bytecode
is executed, the pc automatically
advances over these operand bytes to start the next bytecode in
sequence.
Note |
The next few sections are in "reference manual style," presenting each bytecode separately in all its (often redundant) detail; each bytecode is presented as an operation followed by an explanation. Later sections begin to collapse and coalesce this verbose style into something shorter and more readable. The verbose form is shown at first because the online reference manuals will look more like it, and because it drives home the point that each bytecode "function" comes in many, nearly identical bytecodes, one for each primitive type in Java. |
bipush ... => ..., value
Push 1-byte signed integer. byte1 is interpreted as a signed 8-bit value. This value is expanded to an int and pushed onto the operand stack.
sipush ... => ..., value
Push 2-byte signed integer. byte1 and byte2 are assembled into a signed 16-bit value. This value is expanded to an int and pushed onto the operand stack.
ldc1 ... => ..., item
Push item from constant pool. byte1 is used as an unsigned 8-bit index into the constant pool of the current class. The item at that index is resolved and pushed onto the stack.
ldc2 ... => ..., item
Push item from constant pool. byte1 and byte2 are used to construct an unsigned 16-bit index into the constant pool of the current class. The item at that index is resolved and pushed onto the stack.
ldc2w ... => ..., constant-word1, constant-word2
Push long or double from constant pool. byte1 and byte2 are used to construct an unsigned 16-bit index into the constant pool of the current class. The two-word constant at that index is resolved and pushed onto the stack.
aconst_null ... => ..., null
Push the null object reference onto the stack.
iconst_m1 ... => ..., -1
Push the int -1 onto the stack.
iconst_<I> ... => ..., <I>
Push the int <I> onto the stack. There are six of these bytecodes, one for each of the integers 0-5: iconst_0, iconst_1, iconst_2, iconst_3, iconst_4, and iconst_5.
lconst_<L> ... => ..., <L>-word1, <L>-word2
Push the long <L> onto the stack. There are two of these bytecodes, one for each of the integers 0 and 1: lconst_0 and lconst_1.
fconst_<F> ... => ..., <F>
Push the float <F> onto the stack. There are three of these bytecodes, one for each of the integers 0-2: fconst_0, fconst_1, and fconst_2.
dconst_<D> ... => ..., <D>-word1, <D>-word2
Push the double <D> onto the stack. There are two of these bytecodes, one for each of the integers 0 and 1: dconst_0, and dconst_1.
iload ... => ..., value
Load int from local variable. Local variable byte1 in the current Java frame must contain an int. The value of that variable is pushed onto the operand stack.
iload_<I> ... => ..., value
Load int from local variable. Local variable <I> in the current Java frame must contain an int. The value of that variable is pushed onto the operand stack. There are four of these bytecodes, one for each of the integers 0-3: iload_0, iload_1, iload_2, and iload_3.
lload ... => ..., value-word1, value-word2
Load long from local variable. Local variables byte1 and byte1 + 1 in the current Java frame must together contain a long integer. The values contained in those variables are pushed onto the operand stack.
lload_<L> ... => ..., value-word1, value-word2
Load long from local variable. Local variables <L> and <L> + 1 in the current Java frame must together contain a long integer. The value contained in those variables is pushed onto the operand stack. There are four of these bytecodes, one for each of the integers 0-3: lload_0, lload_1, lload_2, and lload_3.
fload ... => ..., value
Load float from local variable. Local variable byte1 in the current Java frame must contain a single-precision floating-point number. The value of that variable is pushed onto the operand stack.
fload_<F> ... => ..., value
Load float from local variable. Local variable <F> in the current Java frame must contain a single-precision floating-point number. The value of that variable is pushed onto the operand stack. There are four of these bytecodes, one for each of the integers 0-3: fload_0, fload_1, fload_2, and fload_3.
dload ... => ..., value-word1, value-word2
Load double from local variable. Local variables byte1 and byte1 + 1 in the current Java frame must together contain a double-precision floating-point number. The value contained in those variables is pushed onto the operand stack.
dload_<D> ... => ..., value-word1, value-word2
Load double from local variable. Local variables <D> and <D> + 1 in the current Java frame must together contain a double-precision floating-point number. The value contained in those variables is pushed onto the operand stack. There are four of these bytecodes, one for each of the integers 0-3: dload_0, dload1, dload_2, and dload_3.
aload ... => ..., value
Load object reference from local variable. Local variable byte1 in the current Java frame must contain a return address or reference to an object or array. The value of that variable is pushed onto the operand stack.
aload_<A> ... => ..., value
Load object reference from local variable. Local variable <A> in the current Java frame must contain a return address or reference to an object. The value of that variable is pushed onto the operand stack. There are four of these bytecodes, one for each of the integers 0-3: aload_0, aload_1, aload_2, and aload_3.
istore ..., value => ...
Store int into local variable. value must be an int. Local variable byte1 in the current Java frame is set to value.
istore_<I> ..., value => ...
Store int into local variable. value must be an int. Local variable <I> in the current Java frame is set to value. There are four of these bytecodes, one for each of the integers 0-3: istore_0, istore_1, istore_2, and istore_3.
lstore ..., value-word1, value-word2 => ...
Store long into local variable. value must be a long integer. Local variables byte1 and byte1 + 1 in the current Java frame are set to value.
lstore_<L> ..., value-word1, value-word2 => ...
Store long into local variable. value must be a long integer. Local variables <L> and <L> + 1 in the current Java frame are set to value. There are four of these bytecodes, one for each of the integers 0-3: lstore_0, lstore_1, lstore_2, and lstore_3.
fstore ..., value => ...
Store float into local variable. value must be a single-precision floating-point number. Local variable byte1 in the current Java frame is set to value.
fstore_<F> ..., value => ...
Store float into local variable. value must be a single-precision floating-point number. Local variable <F> in the current Java frame is set to value. There are four of these bytecodes, one for each of the integers 0-3: fstore_0, fstore_1, fstore_2, and fstore_3.
dstore ..., value-word1, value-word2 => ...
Store double into local variable. value must be a double-precision floating-point number. Local variables byte1 and byte1 + 1 in the current Java frame are set to value.
dstore_<D> ..., value-word1, value-word2 => ...
Store double into local variable. value must be a double-precision floating-point number. Local variables <D> and <D> + 1 in the current Java frame are set to value. There are four of these bytecodes, one for each of the integers 0-3: dstore_0, dstore_1, dstore_2, and dstore_3.
astore ..., handle => ...
Store object reference into local variable. handle must be a return address or a reference to an object. Local variable byte1 in the current Java frame is set to value.
astore_<A> ..., handle => ...
Store object reference into local variable. handle must be a return address or a reference to an object. Local variable <A> in the current Java frame is set to value. There are four of these bytecodes, one for each of the integers 0-3: astore_0, astore_1, astore_2, and astore_3.
iinc -no change-
Increment local variable by constant. Local variable byte1 in the current Java frame must contain an int. Its value is incremented by the value byte2, where byte2 is treated as a signed 8-bit quantity.
newarray ..., size => result
Allocate new array. size must be an int; it represents the number of elements in the new array. byte1 is an internal code that indicates the type of array to allocate. Possible values for byte1 are as follows: T_BOOLEAN (4), T_chAR (5), T_FLOAT (6), T_DOUBLE (7), T_BYTE (8), T_SHORT (9), T_INT (10), and T_LONG (11).
An attempt is made to allocate a new array of the indicated type, capable of holding size elements. This will be the result. If size is less than zero, a NegativeArraySizeException is thrown. If there is not enough memory to allocate the array, an OutOfMemoryError is thrown. All elements of the array are initialized to their default values.
anewarray ..., size => result
Allocate new array of objects. size must be an int; it represents the number of elements in the new array. byte1 and byte2 are used to construct an index into the constant pool of the current class. The item at that index is resolved. The resulting entry must be a class.
An attempt is made to allocate a new array of the indicated class
type, capable of holding size
elements. This will be the result.
If size is less than 0,
a NegativeArraySizeException
is thrown. If there is not enough memory to allocate the array,
an OutOfMemoryError is thrown.
All elements of the array are initialized to null.
Note |
anewarray is used to create a single dimension of an array of objects. For example, the request new Thread[7] generates the following bytecodes: bipush 7 anewarray can also be used to create the outermost dimension of a multidimensional array. For example, the array declaration new int[6][] generates this: bipush 6 (See the section "Method Signatures" for more information on strings such as "[I".) |
multianewarray ..., size1 size2...sizeN => result
Allocate new multidimensional array. Each size<I> must be an int; each represents the number of elements in a dimension of the array. byte1 and byte2 are used to construct an index into the constant pool of the current class. The item at that index is resolved. The resulting entry must be an array class of one or more dimensions.
byte3 is a positive integer
representing the number of dimensions being created. It must be
less than or equal to the number of dimensions of the array class.
byte3 is also the number
of elements that are popped off the stack. All must be ints
greater than or equal to zero. These are used as the sizes of
the dimensions. An attempt is made to allocate a new array of
the indicated class type, capable of holding size<1>
* size<2> * ... * <sizeN> elements. This
will be the result. If any
of the size<I> arguments
on the stack is less than zero, a NegativeArraySizeException
is thrown. If there is not enough memory to allocate the array,
an OutOfMemoryError is thrown.
Note |
new int[6][3][] generates these bytecodes: bipush 6 It's more efficient to use newarray or anewarray when creating arrays of single dimension. |
arraylength ..., array => ..., length
Get length of array. array must be a reference to an array object. The length of the array is determined and replaces array on the top of the stack. If array is null, a NullPointerException is thrown.
iaload ..., array, index => ..., value laload ..., array, index => ..., value-word1, value-word2 faload ..., array, index => ..., value daload ..., array, index => ..., value-word1, value-word2 aaload ..., array, index => ..., value baload ..., array, index => ..., value caload ..., array, index => ..., value saload ..., array, index => ..., value
Load <type> from array. array must be an array of <type>s. index must be an int. The <type> value at position number index in array is retrieved and pushed onto the top of the stack. If array is null, a NullPointerException is thrown. If index is not within the bounds of array, an ArrayIndexOutOfBoundsException is thrown. <type> is, in turn, int, long, float, double, object reference, byte, char, and short. <type>s long and double have two word values, as you've seen in previous load bytecodes.
iastore ..., array, index, value => ... lastore ..., array, index, value-word1, value-word2 => ... fastore ..., array, index, value => ... dastore ..., array, index, value-word1, value-word2 => ... aastore ..., array, index, value => ... bastore ..., array, index, value => ... castore ..., array, index, value => ... sastore ..., array, index, value => ...
Store into <type> array. array must be an array of <type>s, index must be an int, and value a <type>. The <type> value is stored at position index in array. If array is null, a NullPointerException is thrown. If index is not within the bounds of array, an ArrayIndexOutOfBoundsException is thrown. <type> is, in turn, int, long, float, double, object reference, byte, char, and short. <type>s long and double have two word values, as you've seen in previous store bytecodes.
nop -no change-
Do nothing.
pop ..., any => ...
Pop the top word from the stack.
pop2 ..., any2, any1 => ...
Pop the top two words from the stack.
dup ..., any => ..., any, any
Duplicate the top word on the stack.
dup2 ..., any2, any1 => ..., any2, any1, any2,any1
Duplicate the top two words on the stack.
dup_x1 ..., any2, any1 => ..., any1, any2,any1
Duplicate the top word on the stack and insert the copy two words down in the stack.
dup2_x1 ..., any3, any2, any1 => ..., any2, any1, any3,any2,any1
Duplicate the top two words on the stack and insert the copies two words down in the stack.
dup_x2 ..., any3, any2, any1 => ..., any1, any3,any2,any1
Duplicate the top word on the stack and insert the copy three words down in the stack.
dup2_x2 ..., any4, any3, any2, any1 => ..., any2, any1, any4,any3,any2,any1
Duplicate the top two words on the stack and insert the copies three words down in the stack.
swap ..., any2, any1 => ..., any1, any2
Swap the top two elements on the stack.
iadd ..., v1, v2 => ..., result ladd ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2 fadd ..., v1, v2 => ..., result dadd ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2
v1 and v2 must be <type>s. The vs are added and are replaced on the stack by their <type> sum. <type> is, in turn, int, long, float, and double.
isub ..., v1, v2 => ..., result lsub ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2 fsub ..., v1, v2 => ..., result dsub ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2
v1 and v2 must be <type>s. v2 is subtracted from v1, and both vs are replaced on the stack by their <type> difference. <type> is, in turn, int, long, float, and double.
imul ..., v1, v2 => ..., result lmul ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2 fmul ..., v1, v2 => ..., result dmul ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2
v1 and v2 must be <type>s. Both vs are replaced on the stack by their <type> product. <type> is, in turn, int, long, float, and double.
idiv ..., v1, v2 => ..., result ldiv ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2 fdiv ..., v1, v2 => ..., result ddiv ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2
v1 and v2 must be <type>s. v2 is divided by v1, and both vs are replaced on the stack by their <type> quotient. An attempt to divide by zero results in an ArithmeticException being thrown. <type> is, in turn, int, long, float, and double.
irem ..., v1, v2 => ..., result lrem ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2 frem ..., v1, v2 => ..., result drem ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2
v1 and v2 must be <type>s. v2 is divided by v1, and both vs are replaced on the stack by their <type> remainder. An attempt to divide by zero results in an ArithmeticException being thrown. <type> is, in turn, int, long, float, and double.
ineg ..., value => ..., result lneg ..., value-word1, value-word2 => ..., result-word1, result-word2 fneg ..., value => ..., result dneg ..., value-word1, value-word2 => ..., result-word1, result-word2
value must be a <type>.
It is replaced on the stack by its arithmetic negation. <type>
is, in turn, int, long,
float, and double.
Note |
Now that you're familiar with the look of the bytecodes, the summaries that follow will become shorter and shorter (for space reasons). You can always get any desired level of detail from the full virtual machine specification in the latest Java release. |
ishl ..., v1, v2 => ..., result lshl ..., v1-word1, v1-word2, v2 => ..., r-word1, r-word2 ishr ..., v1, v2 => ..., result lshr ..., v1-word1, v1-word2, v2 => ..., r-word1, r-word2 iushr ..., v1, v2 => ..., result lushr ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2
For types int and long: arithmetic shift left, shift right, and logical shift right.
iand ..., v1, v2 => ..., result land ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2 ior ..., v1, v2 => ..., result lor ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2 ixor ..., v1, v2 => ..., result lxor ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2
For types int and long: bitwise AND, OR, and XOR.
i2l ..., value => ..., result-word1, result-word2 i2f ..., value => ..., result i2d ..., value => ..., result-word1, result-word2 l2i ..., value-word1, value-word2 => ..., result l2f ..., value-word1, value-word2 => ..., result l2d ..., value-word1, value-word2 => ..., result-word1, result-word2 f2i ..., value => ..., result f2l ..., value => ..., result-word1, result-word2 f2d ..., value => ..., result-word1, result-word2 d2i ..., value-word1, value-word2 => ..., result d2l ..., value-word1, value-word2 => ..., result-word1, result-word2 d2f ..., value-word1, value-word2 => ..., result int2byte ..., value => ..., result int2char ..., value => ..., result int2short ..., value => ..., result
These bytecodes convert from a value of type <lhs> to a result of type <rhs>. <lhs> and <rhs> can be any of i, l, f, and d, which represent int, long, float, and double, respectively. The final three bytecodes convert types that are self-explanatory.
ifeq ..., value => ... ifne ..., value => ... iflt ..., value => ... ifgt ..., value => ... ifle ..., value => ... ifge ..., value => ... if_icmpeq ..., value1, value2 => ... if_icmpne ..., value1, value2 => ... if_icmplt ..., value1, value2 => ... if_icmpgt ..., value1, value2 => ... if_icmple ..., value1, value2 => ... if_icmpge ..., value1, value2 => ... ifnull ..., value => ... ifnonnull ..., value => ...
When value <rel> 0 is true in the first set of bytecodes, value1 <rel> value2 is true in the second set, or value is null (or not null) in the third, byte1 and byte2 are used to construct a signed 16-bit offset. Execution proceeds at that offset from the pc. Otherwise, execution proceeds at the bytecode following. <rel> is one of eq, ne, lt, gt, le, and ge, which represent equal, not equal, less than, greater than, less than or equal, and greater than or equal, respectively.
lcmp ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., result fcmpl ..., v1, v2 => ..., result dcmpl ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., result fcmpg ..., v1, v2 => ..., result dcmpg ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., result
v1 and v2 must be long, float, or double. They are both popped from the stack and compared. If v1 is greater than v2, the int value 1 is pushed onto the stack. If v1 is equal to v2, 0 is pushed onto the stack. If v1 is less than v2, -1 is pushed onto the stack. For floating-point, if either v1 or v2 is NaN, -1 is pushed onto the stack for the first pair of bytecodes, +1 for the second pair.
if_acmpeq ..., value1, value2 => ... if_acmpne ..., value1, value2 => ...
Branch if object references are equal/not equal. value1 and value2 must be references to objects. They are both popped from the stack. If value1 is equal/not equal to value2, byte1 and byte2 are used to construct a signed 16-bit offset. Execution proceeds at that offset from the pc. Otherwise, execution proceeds at the bytecode following.
goto -no change- goto_w -no change-
Branch always. byte1 and byte2 (plus byte3 and byte4 for goto_w) are used to construct a signed 16-bit (32-bit) offset. Execution proceeds at that offset from the pc.
jsr ... => ..., return-address jsr-w ... => ..., return-address
Jump subroutine. The address of the bytecode immediately following the jsr is pushed onto the stack. byte1 and byte2 (plus byte3 and byte4 for goto_w) are used to construct a signed 16-bit (32-bit) offset. Execution proceeds at that offset from the pc.
ret -no change- ret2_w -no change-
Return from subroutine. Local variable byte1
(plus byte2 for ret_w
are assembled into a 16-bit index) in the current Java frame must
contain a return address. The contents of that local variable
are written into the pc.
Note |
jsr pushes the address onto the stack, and ret gets it out of a local variable. This asymmetry is intentional. The jsr and ret bytecodes are used in the implementation of Java's finally keyword. |
return ... => [empty]q
Return (void) from method. All values on the operand stack are discarded. The interpreter then returns control to its caller.
ireturn ..., value => [empty] lreturn ..., value-word1, value-word2 => [empty] freturn ..., value => [empty] dreturn ..., value-word1, value-word2 => [empty] areturn ..., value => [empty]
Return <type>
from method. value must be
a <type>. The
value is pushed onto the
stack of the previous execution environment. Any other values
on the operand stack are discarded. The interpreter then returns
control to its caller. <type>
is, in turn, int, long,
float, double,
and object reference.
Note |
The stack behavior of the "return" bytecodes may be confusing to anyone expecting the Java operand stack to be just like the C stack. Java's operand stack actually consists of a number of noncontiguous segments, each corresponding to a method call. A return bytecode empties the Java operand stack segment corresponding to the frame of the returning call, but does not affect the segment of any parent calls. |
tableswitch ..., index => ...
tableswitch is a variable-length bytecode. Immediately after the tableswitch opcode, zero to three 0 bytes are inserted as padding so that the next byte begins at an address that is a multiple of four. After the padding are a series of signed 4-byte quantities: default-offset, low, high, and then (high - low + 1) further signed 4-byte offsets. These offsets are treated as a 0-based jump table.
The index must be an int. If index is less than low or index is greater than high, default-offset is added to the pc. Otherwise, the (index - low)th element of the jump table is extracted and added to the pc.
lookupswitch ..., key => ...
lookupswitch is a variable-length bytecode. Immediately after the lookupswitch opcode, zero to three 0 bytes are inserted as padding so that the next byte begins at an address that is a multiple of four. Immediately after the padding is a series of pairs of signed 4-byte quantities. The first pair is special; it contains the default-offset and the number of pairs that follow. Each subsequent pair consists of a match and an offset.
The key on the stack must be an int. This key is compared to each of the matches. If it is equal to one of them, the corresponding offset is added to the pc. If the key does not match any of the matches, the default-offset is added to the pc.
putfield ..., handle, value => ... putfield ..., handle, value-word1, value-word2 => ...
Set field in object. byte1 and byte2 are used to construct an index into the constant pool of the current class. The constant pool item is a field reference to a class name and a field name. The item is resolved to a field block pointer containing the field's width and offset (both in bytes).
The field at that offset from the start of the instance pointed to by handle will be set to the value on the top of the stack. The first stack picture is for 32-bit, and the second for 64-bit-wide fields. This bytecode handles both. If handle is null, a NullPointerException is thrown. If the specified field is a static field, an IncompatibleClassChangeError is thrown.
getfield ..., handle => ..., value getfield ..., handle => ..., value-word1, value-word2
Fetch field from object. byte1 and byte2 are used to construct an index into the constant pool of the current class. The constant pool item will be a field reference to a class name and a field name. The item is resolved to a field block pointer containing the field's width and offset (both in bytes).
handle must be a reference to an object. The value at offset into the object referenced by handle replaces handle on the top of the stack. The first stack picture is for 32-bit, and the second for 64-bit-wide fields. This bytecode handles both. If the specified field is a static field, an IncompatibleClassChangeError is thrown.
putstatic ..., value => ... putstatic ..., value-word1, value-word2 => ...
Set static field in class. byte1 and byte2 are used to construct an index into the constant pool of the current class. The constant pool item will be a field reference to a static field of a class. That field will be set to have the value on the top of the stack. The first stack picture is for 32-bit, and the second for 64-bit-wide fields. This bytecode handles both. If the specified field is not a static field, an IncompatibleClassChangeError is thrown.
getstatic ..., => ..., value_ getstatic ..., => ..., value-word1, value-word2
Get static field from class. byte1 and byte2 are used to construct an index into the constant pool of the current class. The constant pool item will be a field reference to a static field of a class. The value of that field is placed on the top of the stack. The first stack picture is for 32-bit, and the second for 64-bit-wide fields. This bytecode handles both. If the specified field is not a static field, an IncompatibleClassChangeError is thrown.
invokevirtual ..., handle, [arg1, [arg2, ...]], ... => ...
Invoke instance method based on runtime type. The operand stack must contain a reference to an object and some number of arguments. byte1 and byte2 are used to construct an index into the constant pool of the current class. The item at that index in the constant pool contains the complete method signature. A pointer to the object's method table is retrieved from the object reference. The method signature is looked up in the method table. The method signature is guaranteed to exactly match one of the method signatures in the table.
The result of the lookup is an index into the method table of the named class that's used to look in the method table of the object's runtime type, where a pointer to the method block for the matched method is found. The method block indicates the type of method (native, synchronized, and so on) and the number of arguments (nargs) expected on the operand stack.
If the method is marked synchronized, the monitor associated with handle is entered.
The base of the local variables array for the new Java stack frame is set to point to handle on the stack, making handle and the supplied arguments (arg1, arg2, and so on) the first nargs local variables of the new frame. The total number of local variables used by the method is determined, and the execution environment of the new frame is pushed after leaving sufficient room for the locals. The base of the operand stack for this method invocation is set to the first word after the execution environment. Finally, execution continues with the first bytecode of the matched method.
If handle is null, a NullPointerException is thrown. If during the method invocation a stack overflow is detected, a StackOverflowError is thrown.
invokenonvirtual ..., handle, [arg1, [arg2, ...]], ... => ...
Invoke instance method based on compile-time type. The operand stack must contain a reference (handle) to an object and some number of arguments. byte1 and byte2 are used to construct an index into the constant pool of the current class. The item at that index in the constant pool contains the complete method signature and class. The method signature is looked up in the method table of the class indicated. The method signature is guaranteed to exactly match one of the method signatures in the table.
The result of the lookup is a method block. The method block indicates the type of method (native, synchronized, and so on) and the number of arguments (nargs) expected on the operand stack. (The last three paragraphs are identical to the previous bytecode.)
invokestatic ..., , [arg1, [arg2, ...]], ... => ...
Invoke class (static) method. The operand stack must contain some number of arguments. byte1 and byte2 are used to construct an index into the constant pool of the current class. The item at that index in the constant pool contains the complete method signature and class. The method signature is looked up in the method table of the class indicated. The method signature is guaranteed to match one of the method signatures in the class's method table exactly.
The result of the lookup is a method block. The method block indicates the type of method (native, synchronized, and so on) and the number of arguments (nargs) expected on the operand stack.
If the method is marked synchronized, the monitor associated with the class is entered. (The last two paragraphs are identical to those in invokevirtual, except that no NullPointerException can be thrown.)
invokeinterface ..., handle, [arg1, [arg2, ...]], ...=> ...
Invoke interface method. The operand stack must contain a reference (handle) to an object and some number of arguments. byte1 and byte2 are used to construct an index into the constant pool of the current class. The item at that index in the constant pool contains the complete method signature. A pointer to the object's method table is retrieved from the object reference. The method signature is looked up in the method table. The method signature is guaranteed to exactly match one of the method signatures in the table.
The result of the lookup is a method block. The method block indicates the type of method (native, synchronized, and so on) but, unlike the other "invoke" bytecodes, the number of available arguments (nargs) is taken from byte3; byte4 is reserved for future use. (The last three paragraphs are identical to those in invokevirtual.)
Sathrow ..., handle => [undefined]
Throw exception. handle must be a handle to an exception object. That exception, which must be an instance of Throwable (or a subclass), is thrown. The current Java stack frame is searched for the most recent catch clause that handles the exception. If a matching "catch list" entry is found, the pc is reset to the address indicated by the catch-list pointer, and execution continues there.
If no appropriate catch clause is found in the current stack frame, that frame is popped and the exception is rethrown, starting the process all over again in the parent frame. If handle is null, a NullPointerException is thrown instead.
new ... => ..., handle
Create new object. byte1 and byte2 are used to construct an index into the constant pool of the current class. The item at that index should be a class name that can be resolved to a class pointer. A new instance of that class is then created and a reference (handle) for the instance is placed on the top of the stack.
checkcast ..., handle => ..., [handle | ...]
Make sure object is of given type. handle must be a reference to an object. byte1 and byte2 are used to construct an index into the constant pool of the current class. The string at that index of the constant pool is presumed to be a class name that can be resolved to a class pointer.
checkcast determines whether handle can be cast to a reference to an object of that class. (A null handle can be cast to any class.) If handle can be legally cast, execution proceeds at the next bytecode, and the handle remains on the stack. If not, a ClassCastException is thrown and the stack is emptied.
instanceof ..., handle => ..., result
Determine whether object is of given type. handle must be a reference to an object. byte1 and byte2 are used to construct an index into the constant pool of the current class. The string at that index of the constant pool is presumed to be a class name that can be resolved to a class pointer.
If handle is null, the result is 0 (false). Otherwise, instanceof determines whether handle can be cast to a reference to an object of that class. The result is 1 (true) if it can, and 0 (false) otherwise.
monitorenter ..., handle => ...
Enter monitored region of code. handle must be a reference to an object. The interpreter attempts to obtain exclusive access via a lock mechanism to handle. If another thread already has handle locked, the current thread waits until the handle is unlocked. If the current thread already has handle locked, execution continues normally. If handle has no lock on it, this bytecode obtains an exclusive lock. (A null in either bytecode throws NullPointerException.)
Smonitorexit ..., handle => ...
Exit monitored region of code. handle must be a reference to an object. The lock on handle is released. If this is the last lock that this thread has on that handle (one thread is allowed to have multiple locks on a single handle), other threads that are waiting for handle are allowed to proceed. (A null in either bytecode throws NullPointerException.)
breakpoint -no change-
Call breakpoint handler. The breakpoint bytecode is used to overwrite a bytecode to force control temporarily back to the debugger prior to the effect of the overwritten bytecode. The original bytecode's operands (if any) are not overwritten, and the original bytecode is restored when the breakpoint bytecode is removed.
The following discussion, straight out of the Java virtual machine documentation, shows you an example of the cleverness mentioned earlier that's needed to make a bytecode interpreter fast:
The following set of pseudo-bytecodes, suffixed by _quick, are all variants of standard Java bytecodes. They are used by the runtime to improve the execution speed of the bytecode interpreter. They aren't officially part of the virtual machine specification and are invisible outside a Java virtual machine implementation. However, inside that implementation they have proven to be an effective op-timization.
First, you should know that the javac Java compiler still generates only non-_quick bytecodes. Second, all bytecodes that have a _quick variant reference the constant pool. When _quick optimization is turned on, each non-_quick bytecode (that has a _quick variant) resolves the specified item in the constant pool, signals an error if the item in the constant pool could not be resolved for some reason, turns itself into the _quick variant of itself, and then performs its intended operation.
This is identical to the actions of the non-_quick bytecode, except for the step of overwriting itself with its _quick variant. The _quick variant of a bytecode assumes that the item in the constant pool has already been resolved, and that this resolution did not produce any errors. It simply performs the intended operation on the resolved item.
Thus, as your bytecodes are being interpreted, they are automatically getting faster and faster! Here are all the _quick variants in the current Java runtime:
ldc1_quick ldc2_quick ldc2w_quick anewarray_quick multinewarray_quick putfield_quick putfield2_quick getfield_quick getfield2_quick putstatic_quick putstatic2_quick getstatic_quick getstatic2_quick invokevirtual_quick invokevirtualobject_quick invokenonvirtual_quick invokestatic_quick invokeinterface_quick new_quick checkcast_quick instanceof_quick
If you'd like to go back in this appendix and look at what each
of these does, you can find the name of the original bytecode
on which a _quick variant
is based simply by removing the _quick
from its name. The bytecodes putstatic,
getstatic, putfield,
and getfield have two _quick
variants each, one for each stack picture in their original descriptions.
invokevirtual has two variants:
one for objects and one for arrays (to do fast lookups in java.lang.Object).
Note |
One last note on the _quick optimization, regarding the unusual handling of the constant pool (for detail fanatics only): When a class is read in, an array constant_pool[] of size nconstants is created and assigned to a field in the class. constant_pool[0] is set to point to a dynamically allocated array that indicates which fields in the constant_pool have already been resolved. Constant_pool[1] through constant_pool[nconstants - 1] are set to point at the "type" field that corresponds to this constant item. When a bytecode is executed that references the constant pool, an index is generated, and constant_pool[0] is checked to see whether the index has already been resolved. If so, the value of constant_pool[index] is returned. If not, the value of constant_pool[index] is resolved to be the actual pointer or data, and overwrites whatever value was already in constant_pool[index]. |