JVM memory model
Needless to say, I give you a picture, you must feel familiar with it
- Class loading subsystem: loads. Class bytecode files into memory
- Bytecode execution engine: One of the core components of the JVM that executes instructions contained in bytecode
- Program counter: mainly stores the line number of bytecode executed by the current thread, mainly used as the indicator, branch, loop, jump, exception handling, thread recovery and other basic functions of the program control flow
- Stack: Used to execute Java methods
- Local method stack: For local methods
- Heap: Used to store object instances
- Method area: stores data such as type information, constants, and static variables that have been loaded by VMS
- Direct memory: This piece of memory that is not part of the JVM memory model is native direct memory and is limited by the total native memory size and processor addressing space
The program counter, stack, and local method stack are thread private, while the heap and method area are shared by each thread
The stack
We know from the above that the program counter, stack, and local method stack are thread private, that is, every time a thread is opened, the corresponding blocks of memory are allocated to that thread. As shown in the figure:
The above diagram mainly shows you program counters, stacks, thread-private features of the local method stack, and the interface inside the stack
- Stack frame: inside the stack, when each method is executed, a stack frame will be created and pushed onto the stack. When the method is directly finished, the stack will pop out. When each method is called to the completion of execution, it corresponds to the process of a stack frame from the stack to the stack
- Local variable table: Mainly stores the various Java basic data types and object references that can be placed by the compiler. It stores Pointers to objects
- Operand stack: Implementation of a stack structure that stores operands during bytecode execution
- Dynamic link: A symbolic reference to a method in the runtime constant pool to which the stack belongs, which is held to support dynamic linking during method calls
- Method exit: used to restore its upper method execution state
Let’s go through some code to help you understand the stack
package org.laugen.jvm;
public class Math {
public static int intData = Awesome!;
public static final int constant = 520;
public static User user = new User();
public int compute(a) {
int a = 1;
int b = 2;
int c = (a + b) * 10;
return c;
}
public static void main(String[] args) {
Math math = newMath(); math.compute(); }}Copy the code
When the program runs, it goes through the following sequence of processes:
- Memory into which.class files are loaded through the classloading subsystem
- Create program counters, stacks, and local method stacks for the main thread
- Perform to
main()
Method creates a main stack frame and pushes it onto the stack - Perform to
math.compute()
, creates a compute- stack frame and pushes it onto the stack - after
compute()
Method, compute- stack frame pops out of the stack - after
main()
Method, main- stack frame out of the stack - The thread terminates, destroys the thread and reclaims the memory
Compute (); compute(); compute(); compute(); compute(); First, we decompile this code with the javap -c math.class command and then read the bytecode instructions from the decompile compute() method
Compiled from "Math.java"
public class org.laugen.jvm.Math {
public static int intData;
public static final int constant;
public static org.laugen.jvm.User user;
public org.laugen.jvm.Math();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public int compute(a);
Code:
0: iconst_1
1: istore_1
2: iconst_2
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: bipush 10
9: imul
10: istore_3
11: iload_3
12: ireturn
public static void main(java.lang.String[]);
Code:
0: new #2 // class org/laugen/jvm/Math
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method compute:()I
12: pop
13: return
static {};
Code:
0: sipush Awesome!
3: putstatic #5 // Field intData:I
6: new #6 // class org/laugen/jvm/User
9: dup
10: invokespecial #7 // Method org/laugen/jvm/User."<init>":()V
13: putstatic #8 // Field user:Lorg/laugen/jvm/User;
16: return
}
Copy the code
This is the result of decompilation, so let’s just look at the compute() method here
public int compute(a);
Code:
0: iconst_1
1: istore_1
2: iconst_2
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: bipush 10
9: imul
10: istore_3
11: iload_3
12: ireturn
Copy the code
Here we see that there are 12 instructions executed, and we are now reading one by one. You can download a copy of the JVM’s instruction manual online
0: iconst_1
: pushes int 1 into the operand stack, where the operand stack has only one value, 11: istore_1
: stores int to local variable 1 and pops the value 1 from the operand stackint a = 1;
In this line of code, the local variable a is added to the table and its value is 12: iconst_2
: pushes int 2 onto the operand stack, where the operand stack has only one value, 23: istore_2
: stores int to local variable 1 and pops value 2 from the operand stackint b = 2;
This line of code adds a variable B to the table of local variables and its value is 24: iload_1
: loads int from local variable 1. The value 1 of variable A is pushed into the operand stack, which has only one value 15: iload_2
: loads int from local variable 2. The value 2 of variable b is pushed into the operand stack. The operand stack has two values, 1,2 from the bottom to the top6: iadd
Add int (1, 2); add int (2, 1); add int (3, 3)7: bipush 10
: pushes an 8-bit signed integer into the operand stack. The operand stack has two values, 3,10 from the bottom of the stack to the top9: imul
Int (3, 10); int (3, 10); int (30)10: istore_3
: stores int to local variable 3 and pops the value 30 from the operand stackint c = (a + b) * 10;
This line of code adds a variable c to the table of local variables and its value is 3011: iload_3
: loads int from local variable 3. The value 30 of c is pushed into the operand stack, which has only one value, 3012: ireturn
: Returns int from the method, popping the value 30 from the operand stack as the return value of the method
After analyzing the above procedure, do you have a better understanding of the local variable table and operand stack?
Two points to add:
- The local variable table of the main-stack frame holds the math variable, whose value is the pointer to the math object on the heap
- User is a static variable whose value in the method area is a pointer to the user object on the heap
The heap
The following diagram is the memory model of the heap, and I’ll focus on GC, so I won’t cover GC much here.
In JDK8, the heap of JVM is mainly divided into two chunks, namely the young generation and the old generation. Their proportion is usually young generation: the old generation =1:2, and the young generation is divided into Eden region and two Survivor regions, and their proportion is about E:S1:S2=8:1:1. In general, objects of the young generation will enter the old age if they survive 15 GC.
So, do you know why JVM heaps are young and old?