JVM4: Java runtime data area

As we all know, the Java virtual machine has an automatic memory management mechanism. If there are memory leaks and overflows, it is necessary to understand how the virtual machine uses memory to troubleshoot errors.

The following figure shows the JVM memory layout after JDK8.

The picture is taken from "Code out efficient"

The memory area diagram before JDK8 is as follows:

In HotSpot JVM, the permanent generation is used to store metadata of classes and methods and constant pools, such as Classand Method. Whenever a class is loaded for the first time, its metadata will be placed in the permanent generation.
The permanent generation has a size limit, so if too many classes are loaded, it is likely to cause the permanent generation memory overflow, that is, the evil  java.lang.OutOfMemoryError: PermGen  , for which we have to tune the virtual machine.
So, why is PermGen moved out of HotSpot JVM in Java 8? I summarized two main reasons:

  1. Since PermGen memory often overflows, causing annoying  java.lang.OutOfMemoryError: PermGen , the developers of JVM hope that this memory can be managed more flexibly, and such OOMs should not occur frequently.
  2. Removing PermGen can promote the integration of HotSpot JVM and JRockit VM, because JRockit does not have a permanent generation.
    According to the various reasons above, PermGen was eventually removed, the method area was moved to Metaspace, and the string constant was moved to Java Heap .

Quoted from https://www.sczyh30.com/posts/Java/jvm-metaspace/

Program counter

The Program Counter Register is a small memory space, which can be seen as a line number indicator of the bytecode executed by the current thread.

Since the multithreading of the Java virtual machine is realized by the way that threads alternately switch and allocate processor execution time, at any certain moment, a processor core will only execute instructions in one thread.

Therefore, in order to restore to the correct execution position after the thread is switched, each thread needs to have an independent program counter. The counters between the threads do not affect each other and are stored independently. We call this type of memory area "thread private" Memory.

If the thread is executing a Java method, this counter records the address of the executing virtual machine bytecode instruction; if the thread is executing a Native method, the counter value is empty (Undefined). This memory area is the only area that does not specify any OutOfMemoryError conditions in the Java Virtual Machine Specification.

Java virtual machine stack

Like the program counter, the Java Virtual Machine Stacks (Java Virtual Machine Stacks) are also thread-private, and its life cycle is the same as that of a thread.

The virtual machine stack describes the memory model of Java method execution: each method will create a stack frame (Stack Frame, the basic data structure when the method is running) at the same time as the method is executed, which is used to store the local variable table, operand stack, and dynamics. Links, methods of export and other information. The process of each method from invocation to completion of execution corresponds to the process of pushing a stack frame in the virtual machine stack to popping out of the stack.

In the active thread, only the frame at the top of the stack is valid, which is called the current stack frame. The method being executed is called the current method, and the stack frame is the basic structure of the method running. When the execution engine is running, all instructions can only operate on the current stack frame.

Pushing and popping of the operation stack-"Efficient code output"

1. Local variable table

The local variable table is the area where method parameters and local variables are stored. There is no preparation phase for local variables and must be initialized explicitly. If it is a non-static method, the index[0] position stores the instance reference of the object to which the method belongs . A reference variable occupies 4 bytes, and then the parameters and local variables are stored. The STORE instruction in the bytecode instruction is to write the local variable calculated in the operation stack back into the storage space of the local variable table.

The virtual machine stack specifies two abnormal conditions: if the stack depth requested by the thread is greater than the depth allowed by the virtual machine, a StackOverflowError exception will be thrown; if the virtual machine stack can be dynamically expanded (most current Java virtual machines can be dynamically expanded) , If enough memory cannot be applied for during expansion, OutOfMemoryError will be thrown.

2. Operation stack

The operation stack is a barrel structure stack with an empty initial state. During the execution of the method, various instructions will be
written to and extracted from the stack. The execution engine of the JVM is a stack-based execution engine, where the stack refers to the operation
stack. The definition of the bytecode instruction set is based on the stack type, and the depth of the stack is in the stack attribute of the method meta-information.

The difference between i++ and ++i:

  1. i++: Take out i from the local variable table and push it into the operation stack (load memory), then increment i in the local variable table by 1 (add&store memory), and take out the top value of the operation stack for use, so the thread reads from the operation stack Is the value before the increment.
  2. ++i: First increment the i of the local variable table by 1 (load memory&add&store memory), then take it out and push it into the operation stack (load memory), and then take out the top value of the operation stack and use it. The thread reads from the operation stack. The value after the increment.

I said before that i++ is not an atomic operation. Even if volatile modification is used, it is not thread-safe. It is because i may be taken out of the local variable table (memory) and pushed into the operation stack (register), the operation stack is incremented, and the top of the stack is used. Value update local variable table (register update is written into memory), which is divided into 3 steps. Volatile guarantees visibility and ensures that the latest value is read from the local variable table every time, but these 3 steps may be used by another thread The three-step interruption of, causes the problem of data mutual coverage, which leads to the value of i being smaller than expected.

3. Dynamic Link

Each stack frame contains a reference to the current method in the constant pool, the purpose is to support the dynamic connection of the method call process.

4. Method return address

There are two exit situations when the method is executed:

  1. Normal exit, that is, the normal execution of the return bytecode instructions of any method, such as RETURN, IRETURN, ARETURN, etc.;
  2. Exit abnormally.

Regardless of the exit situation, it will return to the location where the method is currently called. The process of method exit is equivalent to popping the current stack frame. There may be three ways to exit:

  1. The return value is pushed into the upper call stack frame.
  2. The exception information is thrown to a stack frame that can be processed.
  3. The PC counter points to the next instruction after the method call.

Native method stack

The role played by the Native Method Stack and the virtual machine stack is very similar. The difference between them is that the virtual machine stack serves the virtual machine to execute Java methods (that is, bytecode), while the native method stack It is the Native method service used by the virtual machine. The Sun HotSpot virtual machine directly combines the local method stack and the virtual machine stack into one. Like the virtual machine stack, the local method stack area also throws StackOverflowError and OutOfMemoryError exceptions.

When the thread starts to call the local method, it will enter a world that is no longer constrained by the JVM. Native methods can access the data area of ​​the virtual machine at runtime through JNI (Java Native Interface), and can even call registers, with the same capabilities and permissions as JVM. When a large number of native methods appear, it will inevitably weaken the JVM's control of the system, because its error messages are relatively black box. In the case of insufficient memory, the native method stack will still throw nativeheapOutOfMemory.

The most famous of the JNI class native methods is probably  System.currentTimeMillis() that JNI enables Java to make deep use of operating system features and reuse non-Java code. However, in the course of the project, if a large number of other languages ​​are used to implement JNI, cross-platform features will be lost.

Java heap

For most applications, the Java Heap is the largest piece of memory managed by the Java Virtual Machine. The Java heap is a memory area shared by all threads and is created when the virtual machine starts. The sole purpose of this memory area is to store object instances, and almost all object instances allocate memory here.

The heap is the main area managed by the garbage collector, so it is often called the "GC heap" (Garbage Collected Heap). From the perspective of memory recovery, since collectors now basically use generational collection algorithms, the Java heap can also be subdivided into: new generation and old generation; more detailed ones include Eden space, From Survivor space, and To Survivor Space etc. From the perspective of memory allocation, multiple thread-private allocation buffers (Thread Local Allocation Buffer, TLAB) may be divided into the Java heap shared by threads.

The Java heap can be in a physically discontinuous memory space, as long as it is logically continuous. The current mainstream virtual machines are implemented in accordance with scalability (controlled by -Xmx and -Xms). If there is no memory in the heap to complete the instance allocation, and the heap can no longer be expanded, an OutOfMemoryError exception will be thrown.

Method area

The Method Area, like the Java heap, is a memory area shared by each thread. It is used to store data such as class information, constants, static variables, and code compiled by the just-in-time compiler that have been loaded by the virtual machine. Although the
Java virtual machine specification describes the method area as a logical part of the heap, it has an alias called Non-Heap (non-heap), and the purpose should be to distinguish it from the Java heap.

The Java virtual machine specification has very loose restrictions on the method area. In addition to not requiring contiguous memory like the Java heap and can choose a fixed size or expandable, you can also choose not to implement garbage collection. Garbage collection behavior is relatively rare in this area, and its memory recovery goal is mainly for the recovery of the constant pool and the unloading of types. When the method area cannot meet the memory allocation requirements, an OutOfMemoryError exception will be thrown.

Before JDK8, the implementation of the method area in Hotspot was Perm (Perm). JDK8 began to use Metaspace. The string constants of all contents of the previous permanent generation were moved to the heap memory, and other contents were moved to the metaspace. The metaspace was directly in Local memory allocation.

Why use metaspace to replace the implementation of permanent generation?

  1. Strings exist in the permanent generation, which is prone to performance problems and memory overflow.
  2. It is difficult to determine the size of the class and method information, so it is more difficult to specify the size of the permanent generation. If it is too small, the permanent generation will overflow, and if it is too large, it will easily cause the old generation to overflow.
  3. Permanent generation will bring unnecessary complexity to GC, and the recovery efficiency is low.
  4. Combine HotSpot and JRockit into one.

Runtime constant pool

Runtime Constant Pool is part of the method area. In addition to the description information of the class version, fields, methods, interfaces, etc. in the Class file, there is also a constant pool (Constant Pool Table), which is used to store various literal and symbolic references generated during compilation. This part of the content It will be stored in the runtime constant pool in the method area after the class is loaded.

Generally speaking, in addition to saving the symbolic references described in the Class file, the translated direct references are also stored in the runtime constant pool.

Another important feature of the runtime constant pool compared to the class file constant pool is that it is dynamic. The Java language does not require constants to be generated only at compile time, that is, the content of the constant pool in the Class file is not preset to enter the method area. The runtime constant pool, during runtime, new constants may also be put into the pool. This feature is the intern() method of the String class that is more commonly used by developers.

Since the runtime constant pool is a part of the method area, it is naturally limited by the memory of the method area. When the constant pool can no longer apply for memory, an OutOfMemoryError will be thrown.

Direct memory

Direct memory is not a part of the data area of ​​the virtual machine during runtime, nor is it a memory area defined in the Java virtual machine specification.

NIO was newly added in JDK 1.4, and a channel and buffer-based I/O method was introduced. It can use the Native library to directly allocate off-heap memory, and then store it in the Java heap through a The DirectByteBuffer object operates as a reference to this memory. This can significantly improve performance in some scenarios because it avoids copying data back and forth between the Java heap and the Native heap.

Obviously, the allocation of native direct memory will not be limited by the size of the Java heap, but since it is memory, it will definitely be limited by the size of the total native memory (including RAM and SWAP area or paging file) and the addressing space of the processor . When configuring virtual machine parameters, server administrators will set -Xmx and other parameter information according to the actual memory, but often ignore direct memory, making the sum of each memory area larger than the physical memory limit (including physical and operating system-level restrictions), resulting in An OutOfMemoryError exception occurred during dynamic expansion.

Java Threads and Memory-"Efficient Code Output"

Guess you like

Origin blog.csdn.net/zhaofuqiangmycomm/article/details/113850578