A Java virtual machine instruction consists of a byte-length operation code (Opcode) that represents a specific meaning, followed by zero or more operands that represent the operation parameters. Many instructions in the virtual machine do not contain operands, only an opcode. If the exception is ignored, the JVM interpreter can work effectively with just one code.
Copy the code code as follows:
do{
Automatically calculate the PC register and retrieve the opcode from the PC register location
if(operand exists) take out the operand;
Perform the operation defined by the opcode;
}while (process the next loop)
The number and length of operands depend on the opcode. If the length of an operand exceeds one byte, it will be stored in Big-Endian order (high-end first bytecode), and its value should be (byte1<<8) |byte2.
The bytecode instruction stream is single-byte aligned, with the exception of the "tableswitch" and "lookupswitch" instructions. Their operands are special and are divided by 4 bytes. Corresponding gaps need to be reserved to achieve alignment.
Limiting the length of the Java virtual machine opcode to one byte and giving up the parameter length alignment of the compiled code is to obtain short and lean compiled code, even though it may cost the JVM implementation a certain performance cost. Since the opcode can only be one byte in length, it limits the number of instruction sets, and it does not assume that the data is well aligned, which means that when the data exceeds one byte, the specific data structure has to be reconstructed from the bytes. Some performance will be lost.
Data Types and Java Virtual Machine
In the instruction set in the JVM, most instructions contain data type information corresponding to their operations. For example, the iload instruction loads int type data from the local variable table into the operand stack, while fload loads float type data.
For most bytecode instructions related to data types, their opcode mnemonics have special characters to indicate: i represents int type, l represents long, s represents short, b represents byte, c represents char, f stands for float, d stands for double, and a stands for reference. There are separate directives that can be used to convert unsupported types to supported types when necessary.
load and store instructions
Load and store instructions are used to transfer data to and from the stack frame's local variable table and the operand stack.
1) Instructions for loading a local variable into the operand stack include: iload, iload_<n>, lload, lload_<n>, float, fload_<n>, dload, dload_<n>, aload, aload_<n>.
2) Instructions to store a value from the operand stack to the local variable: istore,istore_<n>,lstore,lstore_<n>,fstore,fstore_<n>,dstore,dstore_<n>,astore,astore_<n>
3) Instructions to load constants into the operand stack: bipush, sipush, ldc, ldc_w, ldc2_w, aconst_null, iconst_ml, iconst_<i>, lconst_<l>, fconst_<f>, dconst_<d>
4) Access index instruction of local variable table: wide
Some instructions ending with angle brackets represent a group of instructions, such as iload_<i>, which represents iload_0, iload_1, etc. These groups of instructions are general instructions with one operand.
Operation instructions
Arithmetic instructions are used to perform a specific operation on the values on the two operand stacks and store the result back on the top of the operation stack.
1) Addition instructions: iadd,ladd,fadd,dadd
2) Subtraction instructions: isub, lsub, fsub, dsub
3) Multiplication instructions: imul, lmul, fmul, dmul
4) Division instructions: idiv, ldiv, fdiv, ddiv
5) Remainder instructions: irem, lrem, frem, drem
6) Negative instructions: ineg, length, fneg, dneg
7) Displacement instructions: ishl,ishr,iushr,lshl,lshr,lusr
8) Bitwise OR instructions: ior, lor
9) Bitwise AND instructions: iand, land
10) Bitwise XOR instructions: ixor, lxor
11) Local variable increment instruction: iinc
12) Comparison instructions: dcmpg, dcmpl, fcmpg, fcmpl, lcmp
The Java virtual machine does not clearly stipulate the overflow of integer data, but it stipulates that when processing integer data, only division and remainder instructions will cause the virtual machine to throw an exception when the divisor is 0.
load and store instructions
Load and store instructions are used to transfer data to and from the local variable table of the stack frame and the operand stack.
1) Instructions to load a local variable into the operand stack include: iload, iload_<n>, lload, lload_<n>, float, fload_<n>, dload, dload_<n>, aload, aload_<n>.
2) Instructions to store a value from the operand stack to the local variable: istore,istore_<n>,lstore,lstore_<n>,fstore,fstore_<n>,dstore,dstore_<n>,astore,astore_<n>
3) Instructions to load constants into the operand stack: bipush, sipush, ldc, ldc_w, ldc2_w, aconst_null, iconst_ml, iconst_<i>, lconst_<l>, fconst_<f>, dconst_<d>
4) Access index instruction of local variable table: wide
Some instructions ending with angle brackets represent a group of instructions, such as iload_<i>, which represents iload_0, iload_1, etc. These groups of instructions are general instructions with one operand.
Operation instructions
Arithmetic instructions are used to perform a specific operation on the values on the two operand stacks and store the result back on the top of the operation stack.
1) Addition instructions: iadd,ladd,fadd,dadd
2) Subtraction instructions: isub, lsub, fsub, dsub
3) Multiplication instructions: imul, lmul, fmul, dmul
4) Division instructions: idiv, ldiv, fdiv, ddiv
5) Remainder instructions: irem, lrem, frem, drem
6) Negative instructions: ineg, length, fneg, dneg
7) Displacement instructions: ishl,ishr,iushr,lshl,lshr,lusr
8) Bitwise OR instructions: ior, lor
9) Bitwise AND instructions: iand, land
10) Bitwise XOR instructions: ixor, lxor
11) Local variable increment instruction: iinc
12) Comparison instructions: dcmpg, dcmpl, fcmpg, fcmpl, lcmp
The Java virtual machine does not clearly stipulate the overflow of integer data, but it stipulates that when processing integer data, only division and remainder instructions will cause the virtual machine to throw an exception when the divisor is 0.
type conversion instructions
Type conversion instructions convert two Java virtual machine numerical types to each other. These operations are generally used to implement explicit type conversion operations in user code.
JVM supports widened type conversion (small range type conversion to large range type conversion):
1) Int type to long, float, double type
2) long type to float, double type
3) float to double type
Narrow type conversion instructions: i2b, i2c, i2s, l2i, f2i, f2l, d2l and d2f. Narrow type conversion may cause the conversion results to produce different signs and orders of magnitude. The conversion process may cause the numerical value to lose precision. For example, when int or long type is converted to integer type T, the conversion process only discards the unexpected content of the lowest N bytes (N is the data type length of type T)
Object creation and manipulation
Although class instances and arrays are both objects, the Java virtual machine uses different bytecode instructions for the creation and operation of class instances and arrays.
1) Instruction to create an instance: new
2) Instructions for creating arrays: newarray, anewarray, multianewarray
3) Access field instructions: getfield, putfield, getstatic, putstatic
4) Load array elements into the operand stack instructions: baload, caload, saload, iaload, laload, faload, daload, aaload
5) Store the value of the operand stack into the array element and execute: bastore,castore,castore,sastore,iastore,fastore,dastore,aastore
6) Get the array length instruction: arraylength
7) Check instance type instructions: instanceof, checkcast
Operand stack management instructions
Instructions that directly operate the operand stack: pop, pop2, dup, dup2, dup_x1, dup2_x1, dup_x2, dup2_x2 and swap
control transfer instruction
Lets the JVM conditionally or unconditionally continue program execution from the instruction following the specified instruction rather than the control transfer instruction. Control transfer instructions include:
1) Conditional branch: ifeq, iflt, ifle, ifne, ifgt, ifge, ifnull, ifnotnull, if_cmpeq, if_icmpne, if_icmlt, if_icmpgt, etc.
2) Compound conditional branch: tableswitch, lookupswitch
3) Unconditional branch: goto,goto_w,jsr,jsr_w,ret
There is a special instruction set in the JVM to handle conditional branch comparison operations of int and reference types. In order to clearly indicate whether an entity value is null, there are special instructions to detect null values. Conditional branch comparison operations of boolean type and byte type, char type and short type are all completed using int type comparison instructions, while long, float, and double conditional branch comparison operations are performed by comparison operation instructions of the corresponding type, and the operation instructions will return a The integer value is added to the operand stack, and then a conditional comparison operation of the int type is performed to complete the entire branch jump. All types of comparisons will eventually be converted into int type comparison operations.
Method call and return instructions
invokevirtual instruction: Call the instance method of the object and dispatch according to the actual type of the object (virtual machine dispatch).
invokeinterface instruction: Call the interface method, search for an object that implements this interface method at runtime, and find the appropriate method to call.
invokespecial: Call instance methods that require special processing, including instance initialization methods, private methods and parent class methods
invokestatic: call class method (static)
Method return instructions are distinguished according to the type of return value, including ireturn (the return value is boolean, byte, char, short and int), lreturn, freturn, drturn and areturn. The other return is for void method, instance initialization method, class and The class initialization i method of the interface is used.
synchronous
The JVM supports method-level synchronization and synchronization of a sequence of instructions within a method, both of which are implemented through monitors.
Method-level synchronization is implicit and does not need to be controlled through bytecode instructions. It is implemented in method calls and return operations. The virtual machine distinguishes whether it is a synchronous method from the ACC_SYNCHRONIZED flag in the method standard structure in the method constant pool. When the method is called, the calling instruction will check whether the flag is set. If it is set, the execution thread holds the monitor, then executes the method, and finally releases the monitor when the method is completed.
Synchronize a sequence of instruction sets, usually marked by a synchronized block. The JVM instruction set has monitorenter and monitorexit to support synchronized semantics.
Structured locking is when each monitor exit during a method call matches the previous monitor entry. The JVM ensures that structured locks are established through the following two rules (T represents a thread, M represents a monitor):
1) The number of times T holds M during method execution must be equal to the number of times T releases M when the method is completed.
2) At any time, there will never be a situation where T releases M more times than T holds M.