This is the 19th day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021
In a Java program, each time a thread is enabled, the JVM assigns it a Java stack that holds the running state of the current thread in frames. Today we continue to dig into the Java core and explore stacks and local variables in the JVM.
In Java development, whenever we use new to generate an object in a program, the reference to the object is stored in the stack, and the object is stored in the heap. You can see how important the stack is at the core of Java. Today we continue our series on Java Core, introducing you to stacks, local variables, and their relationships in Java.
In the Java stack
Each time a thread is enabled, the JVM assigns it a Java stack that holds the running state of the current thread in frames. The method being executed by a thread is called the current method, the stack frame used by the current method is called the current frame, the class to which the current method belongs is called the current class, and the constant pool of the current class is called the current constant pool. When a thread executes a method, it keeps track of the current constant pool.
Each time a thread calls a Java method, the JVM pushes a frame on the stack corresponding to that thread, and that frame becomes the current frame. When the method executes, it uses the frame to store parameters, local variables, intermediate results, and so on.
All data on the Java stack is private. No thread can access another thread’s stack data. So we don’t have to worry about stack access synchronization in multi-threaded cases.
Like method areas and heaps, Java stacks and frames do not have to be contiguic in memory; frames can be distributed on contiguic stacks or in the heap
Stack frames are the components of a Java stack
Stack frame consists of three parts: local variable area, operand stack, frame data area. The size of the local variable area and operand stack depends on the corresponding method; they are calculated by word length. But when a method is called, it gets the method’s local variable area and operand stack size from the type information, allocates stack memory accordingly, and pushes it onto the Java stack.
Local variable areas Local variable areas are organized into an array of one-word lengths counting from 0. Values of the types short, byte, and char are converted to int values before being stored in the array. Long and double occupy two consecutive entries in the array. You only need to fetch the index value of the first item of two consecutive items. For example, when a certain long value occupies index 3 or 4 in the local variable area, the instruction only needs to fetch the long value with index 3.
public static int runClassMethod(int i,long l,float f,double d,Object o,byte b) {
return 0;
}
public int runInstanceMethod(char c,double d,short s,boolean b) {
return 0;
}
Copy the code
The storage structure of method parameters and local variables in the local variable area of the above code slice is shown as follows:
There’s nothing to say about the graph above, but if you look at it, you’ll see. However, in this diagram, there is one caveat:
The first item in the runInstanceMethod’s local variable area is a reference, which specifies a reference to the object itself, namely this, but in the runClassMethod method, there is no reference. That’s because runClassMethod is static.
Like the local variable area, the operand stack is organized into an array of word lengths. Unlike the former, however, it is accessed not by index, but by pushing and pushing. The operand stack can be thought of as a storage area for temporary data during computation. Let’s look at the function of the stack with a short program snippet and a picture.
int a = 100;
int b = 98;
int c = a+b;
As you can see from the diagram, the operand stack is actually a temporary data storage area, which is operated by pushing and pushing.
In addition to the local variable area and operand stack, Java stack frames require some data to support constant pool parsing, normal method returns, and exception distribution mechanisms. This data is stored in the Frame data area of the Java stack frame.
When the JVM executes an instruction that requires constant pool data, it accesses it through a pointer to the constant pool in the frame data area.
In addition to handling constant pool parsing, the data in the frame also handles normal and abnormal terminations of Java methods. If it ends normally with a return, the current stack frame pops out of the Java stack, restoring the stack of the method that initiated the call. If the method returns a value, the JVM pushes the return value onto the operand stack that initiated the calling method.
To handle exceptions in a Java method, the frame data area must also hold a reference to the exception reference table for this method. When an exception is thrown, the JVM gives the code in the catch block. If not, the method terminates immediately, and the JVM restores the frame of the calling method with the information from the frame area data. The context in which the method was invoked then rethrows the same exception.
The entire structure of the stack
As described earlier, a stack consists of stack frames, and each time a thread calls a Java method, the JVM pushes a frame into the stack corresponding to that thread. The frame consists of a local variable area, a stack of operands, and a frame data area. So what does a stack look like in a block of code? Here’s an example I took from Inside the JVM for you to see:
Code snippet:
Three snapshots during execution:
The diagram given above only illustrates two things that we can use to understand stacks in Java:
A frame is assigned to the current stack only when a method is called, and then pushed onto the stack.
2. Local data of the corresponding method is stored in the frame. After the method is executed, the corresponding frame pops out of the stack and stores the returned result in the operand stack of the frame calling the method