Java runtime memory allocation

The division of Java memory into heap and Stack is not accurate, and the division of Java memory regions is actually more complex.

When a Java VM executes Java programs, the memory it manages is divided into different data areas:

Above:

  1. HelloWorld.javaIt will be generated by editingHelloWorld.classBytecode files.
  2. Java virtual to accessHelloWorldThis class needs to be passedClassLoaderTo load, willHelloWorld.classThe bytecode file is loaded into JVM memory.
  3. JVM memory can be divided into method areas, heap, virtual machine stack, local method stack, and program counters.

Program counter

Because Java programs are multithreaded, the CPU can divide execution time segments among multiple threads. (In multithreaded execution, multiple threads of execution need to be executed by the CPU to seize resources), so the design counter in the JVM is used to keep track of where code is executed.

Function of program counter

When a thread is suspended by the CPU, you need to record where the code has been executed, so that the CPU can know which line to start executing the thread again.

A program counter is a small memory space in a virtual machine that is used to record the location of the current program execution.

The figure above illustrates the role of the program counter in the CPU. Each thread records where the code is currently executing, and the next time the thread resumes execution, it continues from where the program counter recorded. In addition to sequential execution: branch operations, loop operations, jumps, exception handling, and so on all rely on program counters.

A few notes about program counters:
  1. There are no provisions in the Java Virtual Machine specification for this area of program countersOutOfMemoryErrorSituation (probably not necessary)
  2. Program counters are thread-private; each thread has a private program counter within it that is created with the creation of a thread and dies with the termination of the thread.
  3. When a thread is executing a Java method, this counter records the address of the virtual machine bytecode instruction being executed, if it isNativeMethod, this counter value is Underined.

The virtual machine stack

The virtual machine stack is thread-private and synchronizes with the lifecycle of the thread.

In the Java Virtual Machine specification, two exceptions are specified for this area:

StackOverflowError: Raised when the thread request stack depth exceeds the depth allowed by the virtual machine stack.

OutOfMemoryError: Raised when the Java VIRTUAL machine dynamically expands to insufficient memory.

The JVM is executed by a stack-based interpreter, while DVM is executed by a register-based interpreter.

Stack – based refers to the virtual machine stack. The virtual stack was originally intended to describe the memory model of Java method execution, and each time a method is executed, the JVM creates a stack frame in the virtual stack.

The stack frame

A Stack Frame is a data structure used to support virtual machine method invocation and method execution. Each thread creates a Stack Frame for a method when executing it.

A thread contains multiple stack frames (because multiple methods are executed, each method creates a stack frame), and each stack frame contains: local variable table, operand stack, dynamic link, return address. As shown below:

Local variable scale

A local variable table is a storage space for variable values. Parameters passed when a method is called, as well as local variables created inside the method, are stored in the local variable table.

When Java is compiled into a class file, the max_locals data item in the method Code property table determines the maximum local variable table capacity that the method needs to allocate.

For example:

public static int add(int k){
    int i = 1;
    int j = 2;
    return i + j + k;
}
Copy the code

Compile the above code into a class file using the javac command, and then decomcompile it using the javap -v command. The result is as follows:

  public static int add(int);
    descriptor: (I)I
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: iconst_1
         1: istore_1
         2: iconst_2
         3: istore_2
         4: iload_1
         5: iload_2
         6: iadd
         7: iload_0
         8: iadd
         9: ireturn
      LineNumberTable:
        line 8: 0
        line 9: 2
        line 10: 4
Copy the code

As you can see, the number locals defines is a local variable with a length of 3. Conforms to our definition in the code.

[Note] The system does not assign initial values to local variables. There is no preparation stage like class variables (both instance variables and class variables are assigned initial values).

The operand stack

An Operand Stack, also known as an operation Stack, is a LIFO Stack.

The maximum depth of the operand stack is also written into the max_stacks data item in the method’s Code property table at compile time, and the stack elements can be any Java data type, including long and double.

When a method is first executed, its operand stack is empty, and various bytecode instructions are pushed in and out of the operand stack during the execution of the method. For example, the iadd instruction pops up the top two elements of the operand stack for addition and pushes the result back into the operand stack.

Dynamic link

The main purpose of dynamic linking is to support dynamic linking during method calls.

In a class file, a method calls other methods: symbolic references to these methods need to be converted into direct references to their memory address, and symbolic references exist in the method area.

In the Java virtual machine stack, each stack frame contains a symbolic reference to the method that the stack belongs to in the runtime constant pool.

The return address

When a method is executed, there are only two ways to exit the method:

Normal exit: Code in a method completes normally, or a bytecode instruction (such as return) returned by a method is encountered and exits without throwing any exceptions.

Exception exit: An exception encountered during the execution of a method that is not handled within the method body, causing the method to exit.

Regardless of whether the method exits normally or abnormally, it is returned to the point where it was called. Therefore, the return address in the virtual machine stack is used to help the current method restore its upper-level method execution state.

It is worth noting that:

When exiting normally, the caller’s PC count can be used as the return address and may be stored in the stack frame. When an exception exits, the return address is determined by the exception handler table, and this information is generally not stored in the stack frame.

Instance to explain
public class Hello { public static int add(){ int i = 1; int j = 2; int result = i + j; return result + 10; }}Copy the code

Use the javap command to view the bytecode instructions with the above code:

public static int add(); Descriptor: ()I flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=0 0: iconst_1 Istore_0 // put the top of the operand stack into the local variable table index 1 2: iconst_2 // compress the constant 2 on the top of the operand stack 3: istore_1 // put the top of the operand stack into the local variable table index 2 4: Iload_0 // place the value of local variable table index 1 on the top of the operand stack 5: iloAD_1 // Place the value of local variable table index 2 on the top of the operand stack 6: iadd // place the value of local variable table index 2 on the top of the operand stack 7: Istore_2 // put the value of the local variable table index 2 at the top of the operand stack 8: iload_2 // put the value of the local variable table index 2 at the top of the operand stack 9: bipush 10 // push constant 10 at the top of the operand stack 11: Iadd // Add the top and bottom operands to the top of the stack 12: ireturn // endCopy the code

Instruction details:

Iconst and bipush push constants to the top of the operand stack. The difference is that iconst is used when int is -1 to 5, and bipush is used when int is -128 to 127.

Istore puts the top element of the operand stack into an index position in the local variable table. For example, istore_5 represents placing the top element of the operand stack at subscript 5 in the local variable table.

Iload loads the value of a subscript in the local variable table to the top of the operand stack. For example, iloAD_2 represents pushing a value on the local variable table index 2 to the top of the operand stack.

Iadd stands for addition, which involves adding the top two elements of the operand stack and pushing the result back onto the top of the stack.

By the time **. Java is compiled to **. Class, the size of the local variable table and the depth of the operand stack in the stack frame are already determined and written into the Code property of the method table.

Local method stack

The Native stack is basically the same as the virtual machine stack. It is aimed at Native methods. In the development process, if JNI is involved, it may touch the local stack more.

Heap (Heap)

The heap is the largest area of memory managed by the JVM whose sole purpose is to hold object instances. It is the primary area managed by the Java Garbage Collector (GC), sometimes referred to as the “GC heap.” The concurrent heap is an area of memory shared by all threads. If an object allocated in this area is accessed by multiple threads, thread-safety concerns need to be considered.

Methods area

The method area is a run-time data area specified in the JVM specification.

The method area mainly stores:

  • Information about classes that have been loaded by the JVM (version, fields, methods, interfaces)
  • constant
  • A static variable
  • Just-in-time compiler compiled code
  • data

A method area is an area of memory shared by individual threads.

Method area and permanent area:

The method area is an area specified in the JVM specification, but not the actual implementation. It is important not to confuse the specification with the implementation, and different JVM vendors can have different versions of the “method area” implementation.

For example, HotSpot used a “permanent zone” (or Perm zone) to implement method zones prior to JDK1.7, but the “permanent zone” has been removed since JDK1.8 in place of an implementation called metaspace.

【 summary 】

Method area: a specification level thing that specifies what data should be stored in this area.

Permanent section or Metaspace: is a different implementation of the method section, something at the implementation level.

abnormal

StackOverflowError Stack overflow exception

Recursive calls are a common scenario for StackOverflowError.

public class StackOver { private int number; public static void main(String[] args){ StackOver so = new StackOver(); try { so.method(); } catch(StackOverflowError e){system.out.println (" stack overflow! ); } } public void method(){ number++; method(); }}Copy the code

Each time the method method is called, a stack frame is created in the virtual machine stack. Because the method method is called recursively, it does not exit and does not destroy the stack frame.

OutOfMemoryError Memory overflow exception

Theoretically, outofMemoryErrors can occur in the virtual stack, heap, and method area, but in real projects, most of them occur in the heap:

public class HeapError { public static void main(String[] args){ ArrayList list = new ArrayList(); while (true) { list.add(new HeapError()); }}}Copy the code

conclusion

The five layouts of JVM runtime memory described above are rules defined in the Java Virtual Machine specification, not implementations of virtual machines. There are many virtual machine implementations: HotSpot, JRocket, IBM J9, Dalvik, ART, etc.