JVM data types
Java is statically typed, which affects the design of bytecode instructions such that instructions expect themselves to operate on values of a specific type. For example, there are several add instructions for adding two numbers: iadd, ladd, fadd, dadd. They expect operands of types to be int, long, float, and double, respectively. Most bytecodes have the property of having the same functionality in different forms, depending on the operand types.
JVM-defined data types include
1. 基本类型
- Numeric types: byte (1 byte), short (2 bytes), char (2 bytes), int (4 bytes), float (4 bytes), long (8 bytes) ), double (8 bytes)
- boolean type
- pointer type: instruction type
2. 引用类型
- Class
- Array
- Interface
Support for boolean types in bytecode is limited. For example, there is no structure that can directly manipulate boolean values. The substitution of boolean values to int is done by the compiler, and is ultimately converted to an int structure.
stack-based architecture
The simplicity of the bytecode instruction set is largely due to Sun's design of a stack-based VM architecture rather than a register-based architecture. There are various processes that use JVM-based memory components, but basically only the JVM heap needs to be scrutinized for bytecode instructions:
PC register : For each running thread in a Java program, there is a PC register that holds the address of the currently executing instruction.
Stack : For each thread, a stack is allocated in which local variables, method parameters and return values are stored. When the stack pointer moves down, new memory is allocated; if it moves up, that memory is freed.
Heap : Memory and storage objects (class instances and arrays) shared by all threads. Object reclamation is managed by the garbage collector.
Method area : For each loaded class, it stores the code for the method and a symbol table (eg references to fields or methods) and constant pool.
Bytecode Exploration
opcode | illustrate |
---|---|
iconst_1 | Put the integer variable 1 on the top of the operand stack |
bipush 125 | Convert 125 of type byte to type int and push it onto the stack |
istore_1 | Pop the top element of the stack and store it in the local variable with index 1 |
iload_1 | Get the number in the local variable with index 1 and push it to the top of the stack |
iadd | Pop two elements from the top of the stack, add them, and push the result to the top of the stack |
- When the int value is -1~5, the icont instruction is used
When the int value is 0~5, the JVM uses the icont_0, icont_1, icont_2, icont_3, icont_4, icont_5 instructions to push the constant onto the stack, and when the value is -1, the icont_m1 instruction is used to push the constant onto the stack.
- When the int value is -128~127, the JVM uses the bipush instruction to push the constant onto the stack.
- When the int value is -32768~32767, the JVM uses the sipush instruction to push the constant onto the stack.
- When the int value is -2147483648~2147483647, the JVM uses the ldc instruction to push the constant onto the stack.
Take a chestnut :
public class Text{
public static void main(String[] args){
int a = 1;
int b = 2;
int c = a + b;
}
}
print the compiled bytecode
javac Text.java //首先进行编译
javap -v Text.class //运行javap查看字节码
A lot of results are printed, we only look at the following parts
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V //1
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: iconst_1
1: istore_1
2: iconst_2
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: istore_3
8: return
······
- descriptor: The parameter of the method is an array of strings ([Ljava/lang/String;), and the return value is void (V).
- flags: Indicates that the method is public (ACC_PUBLIC) and static (ACC_STATIC).
Instruction execution description from address 0 to 8:
iconst_1: push the integer variable 1 to the top of the stack
istore_1: Pop the top element of the stack and store it in the local variable with index 1
iconst_2: push the integer variable 2 to the top of the stack
istore_2: pop the top element of the stack and store it in the local variable with index 2
iload_1: Take the number in the local variable with index 1 and push it to the top of the stack
iload_2: Get the top of the push stack in the local variable with index 2
iadd: pop the first two elements on the top of the stack and add, then push the result to the top of the stack
istore_3: pop the top element of the stack and store it in the local variable with index 3
return: return from this void method
It can be seen from the above that each step of the instruction has the precise execution of the JVM.