The overall structure of the JVM

This is the Oracle’s official definition of the JVM memory: docs.oracle.com/javase/spec…

The distribution structure of memory

  • Virtual machine stack: unique to each thread, virtual machine has a stack space for each thread to store local variables of each method in the execution of the current thread. Of course, there are not only local variables, but also operand stack, dynamic connection, method exit;
  • Local method stack: similar to the virtual machine stack, but it is a local variable stored in the execution of native methods. This part is rarely used and need not pay attention to.
  • Program counter: used to record each thread in context switch, record the last execution of symbolic reference, is unique to each thread;
  • Method area: in the JVM specification it is called method area, but in hotspot jdK8 it is implemented using Metaspace, so it can also be called Metaspace. When a class is loaded, it puts the Metaspace information, static variables and static constants of the class in this area.
  • Heap: Heap is familiar to everyone and also needs special attention. It stores new objects. The internal structure of the heap is mainly composed of two parts: new generation and old age, with a default ratio of 1:2. The Cenozoic era is divided into 1 Eden region and 2 Suvivor region, with a default ratio of 8:1:1.

Memory allocation process

The stack

Generate a class file with the following code to analyze how objects are divided in the stack:

public class Math {
    public void compute(a) {
        int a = 10;
        int b = 5;
        int c = (a + b) * 100;
        System.out.println(c);
    }
    public static void main(String[] args) {
        Math math = newMath(); math.compute(); }}Copy the code

Javap -c math. class > math. TXT decompile the class file into math. TXT file.

Compiled from "Math.java"
public class com.demo.jvm.Math {
  public com.demo.jvm.Math();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void compute(a);
    Code:
       0: bipush        10
       2: istore_1
       3: iconst_5
       4: istore_2
       5: iload_1
       6: iload_2
       7: iadd
       8: bipush        100
      10: imul
      11: istore_3
      12: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      15: iload_3
      16: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      19: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #4                  // class com/demo/jvm/Math
       3: dup
       4: invokespecial #5                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #6                  // Method compute:()V
      12: return
}
Copy the code

The result of decompilation is the JVM’s instructions, each of which has its own specific meaning

Compute (); compute(); compute();

  1. bipush 10To push an 8-bit signed integer onto the stack:Put 10 on the operand stack;
  2. istore_1Store a value of int to the local variable 1:Place the value in the operand stack at subscript 1 of the local variable table. When a stack frame is created, a reference to this with subscript 0 is stored in the local variable table by default.;
  3. iconst_5Push int constant 5 onto the stack:Put 5 on the operand stack as in step 1;
  4. istore_2Store int to local variable 2:Place the 5 in the operand stack at subscript 2 in the local variable table;
  5. iload_1Load int from local variable 1:Take 10 out of the local variable table and put it on the operand stack;
  6. iload_2Load int from local variable 2:Take the 5 out of the local variable table and put it on the operand stack;
  7. iaddPerform an addition of int:Add up the values in the operand stack (10 + 5) = 15;
  8. bipush 100To push an 8-bit signed integer onto the stack:Put 100 on the operand stack;
  9. imulPerform multiplication of type int:Multiply the values in the operand stack (15 * 100) = 1500;
  10. istore_3Store int to local variable 3:Place 1500 of the operand stack at subscript 3 of the local variable table;
  11. getstatic #2Get a static field from a class: This instruction is getPrintStreamObject, is the System. Out section of the code, after# 2Is a symbol reference, maintained in the constant pool;
  12. iload_3Load int from local variable 3:Take 1500 out of local variable table 3 and put it on the operand stack;
  13. invokevirtual #3Methods for scheduling objects: call# 3The method of the referenced object, #3 also a symbolic reference, is actually a callPrintStream.printlnThe argument is 1500 in the operand stack;
  14. returnAfter the method is executed, return ends.
  • Operand stack: The value that the execution engine is going to operate on, which is erased after the operation, and then loaded from the local variable table.
  • Local variable table: the value of the local variable to be stored (the basic data type), or in the case of an object, the address reference of the object in the heap (the concept of direct Pointers is also involved here, as discussed later);
  • Dynamic linking: a symbolic reference is converted to a direct address during code execution, and the reference is mutable; I also talked about static linking, and I want to distinguish it from static linking, which happens when a class is loaded, and dynamic linking, which happens when an object executes a method;

Each method corresponds to a stack frame, and infinite recursion will result in a StackOverFlowError. The default stack size is 1M

Methods area

Using the Math code above, execute javap -v math.class to view the description:

Classfile /E:/workspace/learn/blog-demo/target/classes/com/demo/jvm/Math.class
  Last modified 2021-5-19; size 773 bytes
  MD5 checksum 09a14d9236f6ceccf06184daa6cef7d9
  Compiled from "Math.java"
public class com.demo.jvm.Math
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC.ACC_SUPER
Constant pool# 1:= Methodref          #8.#29         // java/lang/Object."<init>":()V
   #2 = Fieldref           #30.#31        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #32.#33        // java/io/PrintStream.println:(I)V
   #4 = Class              #34            // com/demo/jvm/Math
   #5 = Methodref          #4.#29         // com/demo/jvm/Math."<init>":()V
   #6 = Methodref          #4.#35         // com/demo/jvm/Math.compute:()V
   #7 = Fieldref           #4.#36         // com/demo/jvm/Math.math:Lcom/demo/jvm/Math;
   #8 = Class              #37            // java/lang/Object
   #9 = Utf8               math
  #10 = Utf8               Lcom/demo/jvm/Math;
  #11 = Utf8               <init>
  #12 = Utf8               ()V
  #13 = Utf8               Code
  #14 = Utf8               LineNumberTable
  #15 = Utf8               LocalVariableTable
  #16 = Utf8               this
  #17 = Utf8               compute
  #18 = Utf8               a
  #19 = Utf8               I
  #20 = Utf8               b
  #21 = Utf8               c
  #22 = Utf8               main
  #23 = Utf8               ([Ljava/lang/String;)V
  #24 = Utf8               args
  #25 = Utf8               [Ljava/lang/String;
  #26 = Utf8               <clinit>
  #27 = Utf8               SourceFile
  #28 = Utf8               Math.java
  #29 = NameAndType        #11: #12        // "<init>":()V
  #30 = Class              #38            // java/lang/System
  #31 = NameAndType        #39: #40        // out:Ljava/io/PrintStream;
  #32 = Class              #41            // java/io/PrintStream
  #33 = NameAndType        #42: #43        // println:(I)V
  #34 = Utf8               com/demo/jvm/Math
  #35 = NameAndType        #17: #12        // compute:()V
  #36 = NameAndType        #9: #10         // math:Lcom/demo/jvm/Math;
  #37 = Utf8               java/lang/Object
  #38 = Utf8               java/lang/System
  #39 = Utf8               out
  #40 = Utf8               Ljava/io/PrintStream;
  #41 = Utf8               java/io/PrintStream
  #42 = Utf8               println
  #43 = Utf8               (I)V
{
  public com.demo.jvm.Math();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/demo/jvm/Math;

  public void compute(a);
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=1
         0: bipush        10
         2: istore_1
         3: iconst_5
         4: istore_2
         5: iload_1
         6: iload_2
         7: iadd
         8: bipush        100
        10: imul
        11: istore_3
        12: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        15: iload_3
        16: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        19: return
      LineNumberTable:
        line 8: 0
        line 9: 3
        line 10: 5
        line 11: 12
        line 12: 19
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      20     0  this   Lcom/demo/jvm/Math;
            3      17     1     a   I
            5      15     2     b   I
           12       8     3     c   I

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #4                  // class com/demo/jvm/Math
         3: dup
         4: invokespecial #5                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: invokevirtual #6                  // Method compute:()V
        12: return
      LineNumberTable:
        line 15: 0
        line 16: 8
        line 17: 12
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      13     0  args   [Ljava/lang/String;
            8       5     1  math   Lcom/demo/jvm/Math;

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: new           #4                  // class com/demo/jvm/Math
         3: dup
         4: invokespecial #5                  // Method "<init>":()V
         7: putstatic     #7                  // Field math:Lcom/demo/jvm/Math;
        10: return
      LineNumberTable:
        line 5: 0
}
SourceFile: "Math.java"
Copy the code

The Constant pool section is the class’s Constant pool. This information is stored in the method section. As you can see, each instruction has a symbolic reference, which is the class meta information. The default size is 21M, and FullGC will be triggered when the full capacity is used. This part of memory will be dynamically adjusted. If GC reclaimed a large amount of memory last time, it will be automatically adjusted down.

The heap

In general, objects are stored in Eden area after being created. When Eden area is full, MinorGC will be triggered, and the replication algorithm will be used to put the remaining objects into one of Survivor areas, and the creation of objects will be continued -> cleaning. When the objects of the young generation that have been GC for 15 times but have not been recovered are put into the old age, In general, a FullGC is performed when an object cannot be stored in the old generation. (Depending on the specific garbage collector, the CMS sets when to perform FullGC through a concurrent cleanup parameter.) The scope of this GC includes young generation, old generation, and meta space. If there is no available space reclaimed in the old years, the OOM is thrown. You can run the following demo to see the object allocation process using the JVisualVM tool

public class OOMTest {
    private byte[] bytes = new byte[1024 * 1024];
    
    public static void main(String[] args) {
        ArrayList<OOMTest> list = new ArrayList<>();
        for(;;) {
            list.add(new OOMTest());
            try {
                Thread.sleep(30);
            } catch(InterruptedException e) { e.printStackTrace(); }}}}Copy the code

Related JVM parameters in this chapter:

-xx :MaxMetaspaceSize Specifies the maximum memory size of the metasapace. If this parameter is not set, the memory will continue to be expanded until the memory runs out. OOM, set the experience value to 256 MB. -Xss Stack size per thread, default is 1M -Xms initial heap size, default is 1/64 of physical memory -Xmx maximum heap size, Default 1/4 of the physical memory -xMN New generation size -xx :NewSize Sets the initial size of the new generation -xx :NewRatio. Default 2 indicates that the new generation occupies half of the old generation and 1/3 of the total heap memory. -xx :SurvivorRatio Default 8 indicates that a survivor zone occupies 1/8 Eden memory, that is, 1/10 memory of the new generation

The way ahead is so long without ending, yet high and low I’ll search with my will unbending.