JVM series memory model (Java Memory Model)

JVM series memory model (Java Memory Model)

1. Memory model and runtime data area

In the study of the previous chapter , we learned the runtime data area of ​​the java virtual machine according to the position. In this chapter, we learned the Java virtual machine memory model (Java Virtual machine menory model). It can be understood that the jvm runtime database is a kind of Specification, and the JVM memory model is an implementation of the specification

Insert picture description here


The Java virtual machine focuses on storing data in the heap and method areas, so this chapter also focuses on these two aspects for a more detailed description. The heap and method area are shared memory, while the Java virtual machine stack, Native method stack, and program counter are thread-private

Insert picture description here

2. Mind map and legend

Insert picture description here


One is the non-heap area (method area), and the method area is also generally called the "permanent generation". The other is the heap area, which is divided into the young area and the old area. The young area is divided into two parts, one is the Eden area and the other is the Survivor area (S0+S1). The S0 area can also be called the From area, and S1 can also be Call it the To zone

Insert picture description here

3. The object applies for space from the JVM

Insert picture description here

4. Why do we need Survivor area?

Why is the Survivor area needed? Isn't it just Eden?

Assuming that the Survivor area is not designed, the Eden area performs a MinorGC, and the object is directly sent to the Old area, so that the Old area is filled up quickly, and when the Old area is full, FullGC will be performed (the Old area will perform MajorGC, Usually accompanied by MinorGC), FullGC is very time-consuming, so the purpose of designing the Survivor area is to reduce objects being sent to the Old area, there is a transitional Survivor area
Supplement: Minor GC: Cenozoic
Major GC: Old
Generation Full GC: Cenozoic + Old Generation
Eden: S1: S2 is 8:1:1

5. Why do we need two Survivor areas?

The purpose of requiring two Survivor areas is to avoid memory fragmentation. Why do you say that?
Assuming that only one Survivor area is designed, once the Eden area is full, Minor GC will be performed, and the surviving objects in the Eden area will be moved to the Survivor area. When the Eden area is full the next time, the problem will come. The object in the Eden area is hard placed in the Survivor area, which causes the memory occupied by the object to be discontinuous

6. Examples for verification

  • Heap memory overflow

import lombok.Data;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class HeapController {

    List<Foo> list = new ArrayList<Foo>();
    @GetMapping(value = {"heap"})
    public String heapTest() {
        while (true) {
            list.add(new Foo());
        }
    }


    @Data
    class Foo {
        String str;
    }
}

Access interface, memory overflow occurs;

java.lang.OutOfMemoryError: Java heap space
...
Parameters can be set: such as -Xmx64M -Xms512M
  • Method area memory overflow

Use asm, maven configuration:

<dependency>
  <groupId>asm</groupId>
  <artifactId>asm</artifactId>
  <version>3.3.1</version>
</dependency>

Write the code and add the Class information to the method area. Note that the computer performance is not good enough. Do not execute this code. It is easy to cause the computer to restart and consume too much memory. You can also reduce the number of cycles.


import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.util.ArrayList;
import java.util.List;

public class MyMetaspace extends ClassLoader {

  public static List<Class<?>> createClasses() {
    List<Class<?>> classes = new ArrayList<Class<?>>();
    for (int i = 0; i < 10000000; ++i) {
      ClassWriter cw = new ClassWriter(0);
      cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Class" + i, null,
              "java/lang/Object", null);
      MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
              "()V", null, null);
      mw.visitVarInsn(Opcodes.ALOAD, 0);
      mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object",
              "<init>", "()V");
      mw.visitInsn(Opcodes.RETURN);
      mw.visitMaxs(1, 1);
      mw.visitEnd();
      MyMetaspace test = new MyMetaspace();
      byte[] code = cw.toByteArray();
      Class<?> exampleClass = test.defineClass("Class" + i, code, 0,
              code.length);
      classes.add(exampleClass);
    }
    return classes;
  }
}
   

Method area test interface:


import com.example.jvm.jvmexceptionexample.asm.MyMetaspace;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class NonHeapController {

    List<Class<?>> list = new ArrayList<Class<?>>();

    @GetMapping(value = {"/noheap"})
    public String noheap() {
        while (true) {
            list.addAll(MyMetaspace.createClasses());
        }
    }

}
java.lang.OutOfMemoryError: Metaspace
at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.5_54]
Processing method, set the size of Metaspace, such as -XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=512M
  • Java virtual machine stack

In the previous study, the Java virtual machine stack is stored in a stack frame. A method corresponds to a stack frame and is pushed into the stack according to the queue mode. Therefore, if the test program causes problems with the java virtual machine stack, you can test it by recursive method:


import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class StackController {

    public static long count = 0;

    public static void add(long i) {
        count ++ ;
        add(i);
    }

    @GetMapping(value = {"stack"})
    public void stack() {
        add(1);
    }

}

StackOverflow, stack overflow exception:

java.lang.StackOverflowError: null
	at com.example.jvm.jvmexceptionexample.controller.StackController.add(StackController.java:14) ~[classes/:na]

Processing method, set -Xss256k: set the stack size of each thread. After JDK 5, the stack size of each thread is 1M, and the stack size of each thread is 256K before.