How to learn the JVM gracefully

1, Understanding Java virtual machine stack and stack frame

1.1 what is stack frame?

Each stack frame is considered to correspond to a called method, which can be understood as the running space of a method.

Official address: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.6

Components of stack frame:

1. Local Variables: the Local Variables and method parameters in the method are stored in this table. The variables in the Local Variables cannot be used directly. If they need to be used, they must be loaded into the operand stack through relevant instructions as operands.

2. Operand Stack: stores operands in the form of stack pressing and stack out.

3. Dynamic Linking: 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 during method call.

4. Method Return Address: when a method is executed, there are only two ways to exit. One is to encounter the bytecode instruction returned by the method, and the other is to encounter an exception that has not been handled in the method body.

5. Additional information

Person.java

public class Person {
    private String name = "Kevin";
    private int age;
    private final double salary = 100;
    private static String address;
    private final static String hobby = "Programming";
    public void say() {
        System.out.println("person say...");
    }
    public static int calc(int op1,int op2) {
        op1 = 3;
        int result = op1 + op2;
        return result;
    }
    public static void order() {

    }
    public static void main(String[] args) {
        calc(1,2);
        order();
    }
}

Decompilation instruction manual, oracle official website:

https://docs.oracle.com/javase/specs/jvms/se8/html/index.html

Compiled from "Person.java"
public class Person {
...     
 public static int calc(int, int);
  Code:
   0: iconst_3   //Press int type constant 3 into [operand stack]
   1: istore_0   //Store int type value in [local variable 0]
   2: iload_0    //Load int type value from [local variable 0] to stack
   3: iload_1    //Load int type value from [local variable 1] to stack
   4: iadd     //Pop the top element of the stack out of the stack, execute the addition of int type, and the result is put into the stack
   [For example, the iadd instruction (┬žiadd) adds two int values together. It
requires that the int values to be added be the top two values of the operand stack, pushed
there by previous instructions. Both of the int values are popped from the operand stack.
They are added, and their sum is pushed back onto the operand stack. Subcomputations may be
nested on the operand stack, resulting in values that can be used by the encompassing
computation.]
   5: istore_2   //Save int type value at the top of stack to [local variable 2]
   6: iload_2    //Load int type value from [local variable 2] to stack
   7: ireturn    //Return data of type int from method
...
}

The Java virtual machine and stack frame in the example are shown as follows:

1.2 stack pointing heap

If there is a variable in the stack frame, the type is reference type, for example, Object obj = new Object(), this time is a typical stack element pointing to the object in the heap.

1.3 method area pointing pile

Method area will store static variables, constants and other data. If this is the case, it is typical that the elements in the method area point to the objects in the heap.

private static Object obj = new Object();

1.4 pile pointing method area

The method area will contain class information, and there will be objects in the heap. Which class created an object? How does it record information? We need to know the details of a Java object.

1.5 Java object memory layout

The layout of a Java object in memory is divided into three parts:

1. Object header: a series of mark words, class points to the corresponding class metadata of the object, and array Length

2. Instance data: contains all member variables of each object, whose size is determined by the variable type

3. Aligned padding: for object size to be an integer multiple of 8 bytes

2, JVM memory model

2.1 memory model structure

The memory model structure is divided into two parts:

1. Non reactor area:

2. Heap area: one is Old area, the other is Young area

Another: Young is divided into two parts, one is Survivor area (S0+S1), the other is Eden area.

Eden: S0: S1 = 8: 1: 1. S0 is the same size as S1. It can also be called From and To.

2.2 area where the object is created

In general:

1. All newly created objects will be assigned to Eden area

2. Some special large objects will be assigned to Old area

For example: there are objects A, B, C, etc. created in Eden area, but because of the limited memory space of Eden area, its size is only 100M. If 100M is used or A set threshold value is reached, it is necessary to clean up the memory space of Eden area, that is, garbage collection. Such GC is also called Minor GC, Minor GC refers to GC in Young area.

2.3 Survivor area

Survivor is divided into two parts, S0 and S1, which can also be called From and To.

At the same time point, S0 and S1 can only have one area with data, and the other area can only be empty.

For example: next to the GC in the above example, at first, only Eden area and S0 have objects, and S1 is empty. At this time, if a GC operation is performed, the age of the objects in S0 area will be + 1, all the surviving objects in Eden area will be copied to S1 area, and the surviving objects in S0 area will have two destinations.

If the age of the object reaches the previously set age threshold, the object will be moved to the Old area, and the object that does not reach the threshold will be copied to the S1 area. At this time, the Eden area and S0 area have been cleared (there must be no GC objects, and the objects without GC have their own places).

At this time, S0 and S1 exchange roles. The former S0 becomes S1, and the former S1 becomes S0. That is to say, in any case, make sure that the Survivor area named S1 is empty.

Minor GC repeats this process until S1 area is filled, and then copies all objects to Old area.

2.4 Old area

Generally, the Old area is the older object, or the object that exceeds a certain threshold. There will also be GC operations in the Old area. The GC operations in the Old area are called Major GC. The age of the objects that can survive each GC will also be + 1. If they exceed a certain threshold, they will also be recycled.

2.5 how to understand the life of an object

I am a common Java object. I was born in Eden district. I also saw my brother who looks like me in Eden district. We played in Eden district for a long time. One day, there were so many people in Eden district that I was forced to go to S0 District of Survivor district. Since I went to the Survivor District, I began to drift. Sometimes in S0 District of Survivor District, sometimes in S1 District of Survivor District, I had no place to live. Until I was 18 years old, my father said that I was an adult, it was time to go to the society.

So I went to Old district. In the Old generation, there are many people, and they are very Old. I know a lot of people here. I lived in the Old area for 20 years (GC plus one year at a time) and was eventually recycled.

2.6 common problems

1. How to understand Minor/Major/Full GC?

Minor GC: Cenozoic

Major GC: old age

Full GC: new generation + old age

2. Why do we need Survivor? It's only Eden, isn't it?

If there is no Survivor, the Minor GC will be performed once in Eden District, and the surviving objects will be sent to the old age. In this way, the old generation is quickly filled up, triggering the Major GC (because the Major GC is always accompanied by the Minor GC, which can also be regarded as triggering the Full GC).

The space of the old generation is much larger than that of the new generation, and it takes much longer to carry out a Full GC than the Minor GC. What's the harm of long execution time? Frequent Full GC takes a long time, which will affect the execution and response speed of the program.

If we increase or decrease the space of the old age, can we solve the above problems?

If we increase the space of the old age, more living objects can fill the old age. Although the frequency of Full GC is reduced, with the increase of space in the old age, once Full GC occurs, the execution time will be longer. If the space of the old age is reduced, although the time of Full GC is reduced, the old age will soon be filled with living objects, and the frequency of Full GC will increase.

So the significance of Survivor is to reduce the number of objects sent to the old age, and then reduce the occurrence of Full GC. The pre screening of Survivor ensures that only those objects that can survive in the new generation after 16 Minor GC experiences will be sent to the old age.

3. Why do we need two Survivor zones?

The biggest benefit is to solve fragmentation. If there is only one Survivor area, and the newly created object is in Eden, a single Eden is full, and a Minor GC is triggered, the surviving object in Eden will be moved to the Survivor area, and the cycle goes on. The next time Eden is full, the problem will come. At this time, Minor GC, Eden and Survivor have some surviving objects respectively. If the Eden area is alive at this time It is obvious that the memory occupied by these two parts of objects is not continuous, which leads to memory fragmentation. There is always only one Survivor space that is empty, while the other Survivor space is debris free.

4. Why is Eden:S0:S1 8:1:1 in the Cenozoic?

Available memory in the new generation: the memory guaranteed by the replication algorithm is 9:1 Eden:S0 in available memory is 8:1 In the Cenozoic era, Eden:S0:S1 bit 8:1:1

3, Use and verification

3.1 using VisualVM

Use the visual VM tool of JDK to view:

3.2 heap memory overflow

Example code:


import java.util.List;

public class HeapOut {

    public static void main(String[] args) throws Exception {

        List<person> personList = new ArrayList&lt;&gt;();
        while (true) {
            personList.add(new Person());
            Thread.sleep(1);
        }
    }
}

Set the startup parameters: - Xmx20M -Xms20M. After starting the main method, let the program run continuously for a period of time, you can see the following message on the console:

 com.gooagoo.dop.trans.test.json.HeapOut
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:3181)
	at java.util.ArrayList.grow(ArrayList.java:261)
	at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
	at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
	at java.util.ArrayList.add(ArrayList.java:458)
	at com.gooagoo.dop.trans.test.json.HeapOut.main(HeapOut.java:12)

Use visual VM to view:

3.3 method area memory overflow

Example: adding class information to the method area

asm dependence

<dependency>
&nbsp;&nbsp;<groupid>asm</groupid>
&nbsp;&nbsp;<artifactid>asm</artifactid>
&nbsp;&nbsp;<version>3.3.1</version>
</dependency>

Code

package com.gooagoo.dop.trans.test.json;
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<?>&gt; createClasses() {
    List<class<?>&gt; classes = new ArrayList<class<?>&gt;();
    for (int i = 0; i &lt; 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;
    }
    
    
     public static void main(String[] args) throws Exception {
        List<class<?>&gt; list=new ArrayList<class<?>&gt;();
        while(true){
            list.addAll(MyMetaspace.createClasses());
            Thread.sleep(5); 
        }
    }
}

Set the size of the startup parameter Metaspace, such as - XX:MetaspaceSize=50M -XX:MaxMetaspaceSize=50M. After starting the main method, let the program run continuously for a period of time, and you can see the following information on the console:

Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
	at com.gooagoo.dop.trans.test.json.MyMetaspace.createClasses(MyMetaspace.java:21)
	at com.gooagoo.dop.trans.test.json.MyMetaspace.main(MyMetaspace.java:30)

3.4 stack ovewflow

Code example:

public class JvmStack {

    public static long count = 0;
    public static void method(long i) {
        System.out.println(count++);
        method(i);
    }

    public static void main(String[] args) {
        method(1);
    }
}

After starting the main method, let the program run continuously for a period of time, and you can see the following message on the console:

3.5 understanding and explanation

When Stack Space is used to make recursive calls to methods, it is pressed into the Stack Frame. So when the recursive call is too deep, it is possible to run out of Stack Space, so there is a StackOverFlow error.

The size of thread stack is a double-edged sword. If it is set too small, overflow may occur, especially when the thread is recursive and large loop. If it is set too large, it will affect the number of stacks created. If it is a multi-threaded application, memory overflow error will occur.

-Xss128k: set the stack size of each thread. After JDK 5, the stack size of each thread is 1M, and before, the stack size of each thread is 256K. According to the applied line Program to adjust the size of the required memory. In the same physical memory, reducing this value can generate more threads. But the operating system still has the number of threads in a process The experience value is about 3000-5000.

To be continued

</class<?></class<?></init></init></class<?></class<?></class<?></person>

Tags: Programming Java JSON Oracle JDK

Posted on Mon, 10 Feb 2020 10:16:33 -0500 by serbestgezer