Analysis of virtual machine memory management model

In the process of executing a Java program, the Java virtual machine divides the memory managed by the Java program into several different data areas, which can be divided into five parts: virtual machine stack, heap, method area, local method stack, program Counter, as shown in the figure:

virtual machine stack

The Java Virtual Machine Stack (Java Virtual Machine Stack) is private to the thread, and its life cycle is the same as that of the thread. That is, when each method is executed, the Java virtual machine will synchronously create a stack frame (Stack Frame) to store local variable table, operand stack, dynamic connection, method exit and other information. The process of each method being called until the execution is completed corresponds to the process of a stack frame from being pushed to being popped in the virtual machine stack. Let's explain the contents of the virtual machine stack:

local variable table

The local variable table stores various basic data types of the Java virtual machine (boolean, byte, char, short, int, float, long, double) and object reference (reference type, which is not equivalent to the object itself, and may It is a reference pointer pointing to the starting address of the object, or it may point to a handle representing the object or other location related to the object) and returnAddress type (pointing to the address of a bytecode instruction).

The storage space of these data types in the local variable table is represented by a local variable slot (Slot), in which the 64-bit long and double type data will occupy two variable slots, and the rest of the data types will only occupy one. The memory space required by the local variable table is allocated during compilation. When entering a method, how much local variable space the method needs to allocate in the stack frame is completely determined, and the size of the local variable table will not be changed during the running of the method. . Please note that the "size" mentioned here refers to the number of variable slots, how much memory space the virtual machine actually uses (for example, 1 variable slot occupies 32 bits, 64 bits, or more) to implement a variable slot , which is entirely at the discretion of the specific virtual machine implementation.

The returnAddress type is currently rare. It serves bytecode instructions jsr, jsr_w and ret, and points to the address of a bytecode instruction. Although long and double are allocated in two variable slots, because they are inside the thread, there will be no data competition and thread safety issues.

operand stack

The operand stack (Operand Stack) is also often called the operation stack, which is a last in first out (Last In First Out, LIFO) stack. Like the local variable table, the maximum depth of the operand stack is also written into the max_stacks data item of the Code attribute during compilation. When a method just starts to execute, the operand stack of this method is empty. During the execution of the method, there will be various bytecode instructions to write and extract content from the operand stack, that is, popping and Push operation. For example, when doing arithmetic operations, it is performed by pushing the operand stack involved in the operation onto the top of the stack and then calling the operation instruction. For example, when calling other methods, the method parameters are passed through the operand stack. For example, such as the integer addition bytecode instruction iadd, this instruction requires that the two elements closest to the top of the stack in the operand stack have been stored with two int values ​​when running this instruction. , the two int values ​​will be popped and added, and then the result of the addition will be pushed back onto the stack.

Write a small case:

 
 

package com.courage; public class DeOperandStack { public static void main(String[] args) { int i = 1; int j = 2; int k = i + j; } }

At this time, there is only one thread (main) in the DeOperandStack class, and the variables in the local variable table:

The default args is variable 0, so there are four local variables in this thread, so how to use the operand stack to add and subtract?

First push the first constant onto the stack, then store the No. 1 variable in the local variable table, then push the second constant onto the stack, then store the No. 2 variable in the local variable table, and then load the two values ​​​​of the local variable table 1 and 2 Push the stack, pop the sum and push the result onto the stack, and then store the top data on the stack to the No. 3 variable.

dynamic link

Each stack frame contains a reference to the method to which the stack frame belongs in the runtime constant pool. This reference is held to support dynamic linking (Dynamic Linking) during the method call. We know that there are a large number of symbolic references stored in the constant pool of the Class file, and the method call instruction in the bytecode uses the symbolic reference pointing to the method in the constant pool as a parameter. Some of these symbolic references will be converted into direct references during the class loading phase or when they are used for the first time. This conversion is called static resolution. The other part will be converted into direct references during each run, this part is called dynamic linking.

method export

After a method starts executing, there are only two ways to exit the method:

  • A bytecode instruction returned by a method is encountered
  • Encountered an exception

The first way is that the execution engine encounters any bytecode instruction returned by any method. At this time, the return value may be passed to the upper-level method caller (the method that calls the current method is called the caller or calling method), the method Whether there is a return value and the type of the return value will be determined according to which method return instruction is encountered. This way of exiting the method is called "Normal Method Invocation Completion".

Another way to exit is that an exception is encountered during method execution, and this exception has not been properly handled in the method body. Whether it is an exception generated inside the Java virtual machine or an exception generated by using the athrow bytecode instruction in the code, as long as no matching exception handler is found in the exception table of this method, the method will exit. This exit method The method is called "abrupt method invocation completion (Abrupt Method Invocation Completion)".

A method that exits with an exception completion exit does not provide any return value to its upper-level caller. No matter what exit method is used, after the method exits, it must return to the position where the original method was called before the program can continue to execute. When the method returns, it may need to save some information in the stack frame to help restore its upper-level main The execution status of the calling method.

heap

The Java heap is a memory area shared by all threads and created when the virtual machine starts. The sole purpose of this memory area is to store object instances, and "almost" all object instances and arrays in the Java
world allocate memory here.

From the perspective of memory allocation, multiple thread-private allocation buffers (Thread Local Allocation Buffer, TLAB) can be divided into the Java heap shared by all threads to improve the efficiency of object allocation. However, no matter from what point of view, no matter how divided, it will not change the commonality of the storage content in the Java heap. No matter which area, it can only store object instances. The purpose of subdividing the Java heap is only for better recycling. memory, or allocate memory faster.

method area

Method Area (Method Area), like the Java heap, is a memory area shared by each thread. It is used to store data such as type information, constants, static variables, and code caches compiled by the instant compiler that have been loaded by the virtual machine.

Like the Java heap, it does not require continuous memory and can choose a fixed size or expandable. You can even choose not to implement garbage collection. The memory recovery goal in this area is mainly for the recovery of the constant pool and the unloading of types. Generally speaking The recovery effect of this area is relatively difficult to be satisfactory, especially the unloading of types, the conditions are quite harsh, but the recovery of this part of the area is sometimes necessary, and the incomplete recovery of this area will lead to memory leaks.

The relationship between method area, permanent generation and metaspace

The reason why these three are put together is that it is easy to confuse here. For the Hotspot virtual machine, the method area is  PermGen(permanent generation) in JDK6 and JDK7, and the method area is  Metaspace(metaspace) in JDK8. What's going on?

The method area is the specification of the JVM, and all virtual machines must abide by it. Common JVM virtual machines Hotspot, JRockit (Oracle), J9 (IBM)

PermGen space is the implementation of the method area of ​​the HotSpot virtual machine based on the JVM specification, and only HotSpot has the PermGen space. Virtual machines such as JRockit (Oracle) and J9 (IBM) have method areas, but there is no PermGen space.

PermGen space is JDK7 and before, a landing implementation of HotSpot virtual machine for the method area, which was removed in JDK8.

Metaspace (metaspace) is a new implementation of the method area of ​​the HotSpot virtual machine in JDK8 and later.

The permanent generation and metaspace can be used to store long-lived objects in the heap. The biggest difference between the metaspace and the permanent generation is that the metaspace is not in the virtual machine, but uses local memory

class information

Each class has a Class object, which is generated during compilation and saved in a .class file with the same name. These Class objects contain detailed information such as the parent class, interface, constructor, method, and attribute of this type. These class files will be loaded into the JVM by the ClassLoader when the program is running, and they will be represented as a Class object in the JVM. The JVM uses The Class object creates all regular objects of this class, and the information of this object is stored in the class information of the method area.

constant pool

The Runtime Constant Pool is part of the method area. In addition to the class version, fields, methods, interfaces and other description information in the Class file, there is also a constant pool table (Constant Pool Table), which is used to store various literals and symbol references generated during compilation. This part The content will be stored in the runtime constant pool in the method area after the class is loaded . Since the runtime constant pool is part of the method area, it is naturally limited by the memory in the method area. When the constant pool can no longer apply for memory, an OutOfMemoryError exception will be thrown.

static variable area

Static variables are also called class variables, and all instances of the class are shared. This area is dedicated to storing static variables and static blocks.

Static modified ones are loaded into memory when the JVM is running, so no instance class is needed.

Static variables allocate memory and set the initial value of class variables in the preparation phase of class loading. Conceptually, the memory used by these variables should be allocated in the method area, but it must be noted that the method area itself is a logical In JDK 7 and before, when HotSpot uses the permanent generation to implement the method area, the implementation fully conforms to this logical concept; while in JDK 8 and later, class variables will be stored in the Java heap along with the Class object , at this time, "class variables are in the method area" is completely an expression of logical concepts. Regarding this part, the author has introduced and verified it in Section 4.3.1.

program counter

The program counter (Program Counter Register) is a small memory space, which can be regarded as the line number indicator of the bytecode executed by the current thread. In the conceptual model of the Java virtual machine, the bytecode interpreter selects the next bytecode instruction to be executed by changing the value of the counter when it works. It is an indicator of the program control flow, branch, loop, jump , exception handling, thread recovery and other basic functions need to rely on this counter to complete.

Since the multithreading of the Java virtual machine is realized by switching threads in turn and allocating processor execution time, at any given moment, a processor (a core for a multi-core processor) will only execute one instructions in the thread. Therefore, in order to return to the correct execution position after thread switching, each thread needs to have an independent program counter. The counters between threads do not affect each other and are stored independently. We call this type of memory area "thread private". of memory.

native method stack

Native Method Stacks (Native Method Stacks) are very similar to virtual machine stacks. The difference is that the virtual machine stack serves the virtual machine to execute Java methods (that is, bytecodes), while the native method stack serves the virtual machine. The local (Native) method service used by the machine.

Explanation: This article is limited in length, so only part of the interview content is shown. The complete Java interview learning document has been compiled for you. If you need a friend, private message me (need) to receive Java and Dachang interview learning materials for free!

 

 

Guess you like

Origin blog.csdn.net/m0_67788957/article/details/123737648