0 the introduction

What is a virtual machine bytecode execution engine? What does it do?

Before addressing this issue, consider the.class file structure and the class loading mechanism.

The Java program is compiled using the Javac command, and the resulting virtual machine bytecode is stored in a.class file. The classloading mechanism is the process of loading bytecode from a.class file into the JVM’s method area and generating class objects for that class (again, not instantiation objects for that class).

1 Vm bytecode execution engine

Suppose we now have a class like this:

public class Demo {

    public static void main(String[] args) {
        System.out.println("hello world"); }}Copy the code

The JVM takes the main methods as its entry point, and these methods are called before they can be run. The virtual machine bytecode execution engine is one of the parts of the JVM that calls and runs the methods.

2 run time stack frame structure

To understand how a JVM calls a method, you need to first understand the concept of stack frames. Stack frame is a kind of data structure, which includes local variable table, operand stack, dynamic link, return address and other information.

  • The stack frame exists in the virtual machine stack and is the unit element in the virtual machine stack
  • Different stack frames in each thread correspond to different methods called by that thread. As you can see, there are many stack frames and many method chains called
  • In an active thread, only the current stack frame is valid and corresponds to the currently executing method, which is called the current method
  • Each time a new method is called, the corresponding stack frame is pushed to the top of the stack, i.e. becomes the new current stack frame. When a method exits, the stack frame corresponding to the method is destroyed.

The size of the local variable table and the depth of the operand stack is already determined when the stack frame is compiled into a.class file. This information is stored in the code attribute in the method table, so the amount of memory allocated to each stack frame is not affected by the runtime variable data.

Review the method table and the definition of the code attribute.

  • Method tables: The method tables in the.class file contain information about this method: access flags (public, private, and so on), name indexes (pointing to the constant pool), descriptor indexes (pointing to the constant pool, which describes the parameter list and return value of the method), and property list collections.
  • Definition of the code attribute: The code attribute is stored in a property table, which is a collection of multiple attributes. The code property stores the code inside Java methods that the compiler compiles into bytecode instructions (which record the size of the local variator table and the depth of the operand stack).

It is not necessary to have a property table in the method table, because if this is an abstract method, there is no need for a property table to exist in the method table generated by this method (the Java method is not defined, and other properties in the property table cannot be generated).

3. Local variation scale

Since each stack frame corresponds to each method called, the stack frame should store Java code that we would normally write in the method body.

As part of the stack frame, the local variable table is used to store method parameters and local variables defined within the method. When the Java program is compiled into a.class file, the max_locals data item in the code property determines the maximum size of the local variable table to be allocated by the method.

The local variable table is organized as an array counting from 0 in units of one slot. The virtual machine specification does not specify the slot size, but states that each slot should hold a Boolean, byte, CHAR, short, int, float, Reference, or returnAddress.

Short, byte, and char are converted to int before being stored in the array. Long and double occupy two consecutive items in the array. When accessing a long or double in a local variable, simply fetch the index of the first of the two consecutive items. For example, if a long value occupies 3 or 4 indexes in the local variable area, the instruction only needs to take the long value with index 3.

Here’s an example:

public static int runClassMethod(int i,long l,float f,double d,Object o,byte b {
    return 0;
}
Copy the code
public int runInstanceMethod(char c,double d,short s,boolean b) {
    return 0;
}
Copy the code

As shown in the figure above, you can see that the virtual machine uses the local variable table to complete the process of passing parameter values to the parameter variable list. And the first item in the runInstanceMethod’s local variable list is a reference, which specifies a reference to the object itself, which is commonly known as this, but in the RunClassMethod method, there is no reference. Because runClassMethod is a static method.

The reference type represents a reference to an object instance, which should do two things:

  1. The starting address index of the object in the Java heap is found from this reference, either directly (direct reference) or indirectly (handle pool)
  2. The type information stored in the method area from a reference to the type of the object, either directly (object header) or indirectly (handle pool)

4 Operand stack

Similarly, the operand stack is a first-in, last-out stack structure. As with the local variable table, the maximum depth of the operand stack is written into the max_stacks data item of the code attribute at compile time and does not change at run time.

The operand stack, like the local variable area, is organized into an array. Each element in the operand stack can be of any Java data type. 32-bit data types have a stack size of 1 and 64-bit data types have a stack size of 2. 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.

Here is a picture to understand the function of the lower operation stack:

If you don’t understand the instructions shown above, don’t worry, more details will be covered in Part 3

5 Dynamic Connection

This part will be covered in part 2, but not now.

6 Method returns the address

After a method is executed, it is bound to exit in the future. The exit of a method can be divided into normal end and abnormal end. If it ends normally with a return, the current stack frame pops out of the stack owned by the thread, restoring the stack of the method that initiated the call. If a method has a return value, the JVM pushes the return value into the operand stack that initiated the calling method.

To handle exceptions in a Java method, a reference to the method exception reference table must also be stored in the stack frame. When an exception is thrown, the JVM calls the code in the catch block, and if no catch block is found, the method terminates immediately. The JVM then uses the information in the stack frame to restore the frame of the calling method, and then re-throws the same exception from the context in which the calling method was originated.

7 summary

  1. Each thread has its own stack
  2. A stack consists of stack frames
  3. Each method call corresponds to a stack frame, and the currently executing method corresponds to the current stack frame
  4. A stack frame is a data structure consisting of local variable table, operand stack, method return address, and other stack frame information
  5. Instead of storing variable names in the local variable table, indexes are used to access values stored in the local variable table
  6. The operand stack is just a temporary storage area that, like the stack frame, uses a stack-like data structure

8 Reference Reading

  1. In-depth understanding of the Java virtual machine
  2. Drill down into the JVM — stacks and local variables