JVM garbage collector and memory allocation strategy one (JVM heap, stack, method area memory overflow case)


Preface

Java and C++ directly have a high wall surrounded by dynamic memory allocation and garbage collection technology. People outside the wall want to go in, and people inside the wall want to come in. As a java engineer, I will take you to explore the secrets of the wall. .
This article is the first article of the JVM garbage collector and memory allocation strategy module learning. It is mainly used as a pavement before the real learning. It mainly introduces the JVM runtime data area and the scenarios under which memory overflow occurs in each area. The following will simulate and analyze the memory overflow scenarios of various areas of the JVM through actual code combat.


JVM runtime data area

Insert picture description here

Program counter

The program counter is a small memory area used to record the address of the virtual machine bytecode instructions being executed by the current thread. In the case of multi-threading, in order for the current thread to quickly recover to the last executed code position after acquiring CPU resources, each thread must have its own separate program counter, and the program counters between each thread do not affect each other , Independent storage, so the program counter belongs to the thread isolation data area. This area is also the only place where OutOfMemoryError is not mentioned in the "Java Virtual Machine Specification".

Virtual machine stack

The virtual machine stack is also private to the thread. Each thread will have a virtual machine stack. The life cycle is the same as that of the thread. If the current thread does not call a method, a stack frame will be created to store the local variable table, operand stack, dynamic link, and method. For export and other information, the process from when each method is called and executed to the completion of execution corresponds to the process of pushing this stack frame in the virtual machine stack to popping out of the stack.
Two exceptions are defined for this area in the "Java Virtual Machine Specification": 1. If the depth of the stack requested by the thread is greater than the maximum depth allowed by the virtual machine, a Stack
OverflowError exception will be thrown back ; 2. In the support of stack dynamics In the expanded virtual machine, if it fails to apply for sufficient memory during automatic stack expansion, OutOfMemoryError will be thrown back (if the current virtual machine does not support dynamic stack expansion, a StackOverflowError will be thrown when the remaining space in the stack cannot create a new stack frame)

Native method stack

The native method stack is similar to the virtual machine stack, the virtual machine stack is for the java method, and the local method stack is for the native method.

heap

The heap is the largest piece of memory managed by the virtual machine. It is a memory area shared by threads. It is created when the virtual machine starts to store object instances created during the running of the virtual machine. It is also a key area for garbage collection and memory fragmentation. The new generation, old generation, Eden area, etc. mentioned in the above figure are not the further detailed division of the heap in the "Java Virtual Machine Specification", but the logical division of the heap based on the generational design of the garbage collector. From the perspective of the design of the garbage collector, the detailed division of the heap in the above figure is open to discussion. This section will really analyze the different garbage collectors in detail later. From the perspective of memory allocation, the heap shared by all threads can also be divided into multiple thread-private allocation buffers (TLAB) to improve the efficiency of object allocation. The purpose of dividing the java heap is to better recover memory and Allocate memory faster.

Method area

The method area, like the heap, is also a memory area shared by threads, which is mainly used to store data such as type information, constants, and static variables loaded by the JVM. Speaking of the method area, the concept of "permanent generation" must be mentioned. Before JDK8, the permanent generation was used in the HotSpot virtual machine to implement the method area, that is, the method area is placed in the heap, which saves the need to write separate garbage collection and memory management for the method area. However, this design makes the virtual machine more prone to memory overflow. The reference permanent code has an upper limit of -XX:MaxPerSize, even if it is not configured, there will be a default upper limit, and as the constant pool becomes more and more constant More and more types of information are loaded, and because the types of unloading and recycling conditions are more demanding, there will be a greater chance of causing memory overflow. After JDK8, the meta space in the local memory is used to implement the method area, so that the method area can be dynamically expanded infinitely as long as it does not touch the upper limit of the operating system memory. It is stipulated in the "Java Virtual Machine Specification" that the OutOfMemoryError exception will be thrown when the method area cannot meet the memory allocation requirements.

Direct memory

Strictly speaking, direct memory is not a part of the jvm runtime data area, nor is it the memory area defined in the "Java Virtual Machine Specification". But this part of the memory area is also frequently used and can also cause OutOfMemoryError to appear. This area is mainly used to store the Socket. When the java program needs to communicate with other services such as the database, it needs to create a Socket for the sending and receiving of information. The socket is at the operating system level, and the jvm receives the java program's create connection instruction Then, the function of the operating system is called to directly allocate the off-heap memory, and then a DirectByteBuffer object stored in the java heap is used as a reference to this memory to operate. Although the direct memory is not limited by the virtual machine, it is limited by the upper limit of the operating system's memory just like the method area, and also throws OutOfMemoryError when the memory does not meet the allocation.

Virtual machine stack OutOfMemoryError, StackOverflowError

Regarding these two exceptions on the stack, it must be clear under what circumstances will cause the kind of exception:

  1. The stack does not support dynamic expansion, and the thread stack request stack depth exceeds the maximum depth of the virtual machine stack StackOverflowError
  2. The stack supports dynamic expansion (multi-threaded scenarios and single-threaded scenarios lead to different processing ideas for OutOfMemoryError) OutOfMemoryError
    PS: The stack supports dynamic expansion of the virtual machine itself, and this verification is best performed on a server that limits the maximum memory of a single process, such as 32G Windows Operating system, otherwise, if the stack supports dynamic expansion, the size of the stack is limited by the operating system. My computer will have to wait a long time for 16G to produce results... Mainly I don’t bother to download Classic and other virtual machines that support dynamic expansion. Of course, there is a tuning method for the second scenario: if the OutOfMemoryError is caused in the single-threaded scenario, it means that the maximum depth of the stack does not meet the depth required by the thread when the internal pressure is squeezed to the extreme. To increase the depth of the stack, you can try to increase the depth of the stack by reducing the heap memory or reducing the size of a single stack frame. If you are in a multi-threaded scenario, it must be the upper limit of the memory and the OOM caused by the inability to create a stack for a new thread At this time, you can reduce the heap memory or reduce the depth of a single stack in exchange for more threads.
    Next, verify that the first scenario stack does not support dynamic expansion, and the thread stack request stack depth exceeds the maximum depth of the virtual machine stack, resulting in StackOverflowError.
public class StackSpace {
    
    
    int i=0;
    public void demo(){
    
    
        System.out.println(i);
        i++;
        demo();
    }

    public static void main(String[] args) {
    
    
        StackSpace stackSpace=new StackSpace();
        stackSpace.demo();
    }

}

Print result:

0
...
9866
9867
9868
9869
9870
9871
Exception in thread "main" java.lang.StackOverflowError
	at java.base/java.nio.Buffer.<init>(Buffer.java:222)
	at java.base/java.nio.CharBuffer.<init>(CharBuffer.java:281)
	at java.base/java.nio.HeapCharBuffer.<init>(HeapCharBuffer.java:75)
	at java.base/java.nio.CharBuffer.wrap(CharBuffer.java:393)
	at java.base/sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:280)
	at java.base/sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
	at java.base/java.io.OutputStreamWriter.write(OutputStreamWriter.java:211)
	at java.base/java.io.BufferedWriter.flushBuffer(BufferedWriter.java:120)
	at java.base/java.io.PrintStream.write(PrintStream.java:605)
	at java.base/java.io.PrintStream.print(PrintStream.java:676)
	at java.base/java.io.PrintStream.println(PrintStream.java:812)
	at com.oom.StackSpace.demo(StackSpace.java:9)
	at com.oom.StackSpace.demo(StackSpace.java:11)

堆OutOfMemoryError

In order to make the heap memory overflow faster, we need to configure some parameters of the JVM to make the heap memory smaller

**
 * -Xmx20m -Xms20m -Xmn10m -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails
 * HeapSpace outOfMemoryError JVM堆内存溢出Demo
 * JVM参数:
 * -Xmx20m 最大堆内存
 * -Xms20m 最小堆内存
 * -Xmn10m 新生代内存
 * -XX:SurvivorRatio=8 新生代中Eden区与Survivor区比值
 * -XX:+HeapDumpOnOutOfMemoryError 堆内存溢出快照
 * -XX:+PrintGCDetails GC日志打印
 */
public class HeapSpace {
    
    
    static class Demo{
    
    

    }

    public static void main(String[] args) {
    
    

        List<Demo> demos=new ArrayList<>();

        while (true){
    
    
            demos.add(new Demo());

        }
    }

}

Print result:

[GC (Allocation Failure) [PSYoungGen: 8192K->1008K(9216K)] 8192K->5121K(19456K), 0.0090997 secs] [Times: user=0.03 sys=0.01, real=0.01 secs] 
[GC (Allocation Failure) --[PSYoungGen: 9200K->9200K(9216K)] 13313K->19432K(19456K), 0.0120218 secs] [Times: user=0.06 sys=0.00, real=0.02 secs] 
[Full GC (Ergonomics) [PSYoungGen: 9200K->0K(9216K)] [ParOldGen: 10232K->9836K(10240K)] 19432K->9836K(19456K), [Metaspace: 3190K->3190K(1056768K)], 0.1015416 secs] [Times: user=0.38 sys=0.01, real=0.10 secs] 
[Full GC (Ergonomics) [PSYoungGen: 7742K->8020K(9216K)] [ParOldGen: 9836K->7725K(10240K)] 17578K->15746K(19456K), [Metaspace: 3195K->3195K(1056768K)], 0.1425264 secs] [Times: user=0.77 sys=0.01, real=0.14 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 8020K->8006K(9216K)] [ParOldGen: 7725K->7725K(10240K)] 15746K->15731K(19456K), [Metaspace: 3195K->3195K(1056768K)], 0.1086842 secs] [Times: user=0.65 sys=0.00, real=0.11 secs] 
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid7963.hprof ...
Heap dump file created [27802776 bytes in 0.091 secs]
Heap
 PSYoungGen      total 9216K, used 8192K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 8192K, 100% used [0x00000007bf600000,0x00000007bfe00000,0x00000007bfe00000)
  from space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
  to   space 1024K, 46% used [0x00000007bff00000,0x00000007bff76e30,0x00000007c0000000)
 ParOldGen       total 10240K, used 7725K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
  object space 10240K, 75% used [0x00000007bec00000,0x00000007bf38b770,0x00000007bf600000)
 Metaspace       used 3227K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 358K, capacity 388K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:3210)
	at java.util.Arrays.copyOf(Arrays.java:3181)
	at java.util.ArrayList.grow(ArrayList.java:267)
	at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:241)
	at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:233)
	at java.util.ArrayList.add(ArrayList.java:464)
	at com.oom.HeapSpace.main(HeapSpace.java:27)

Method area OutOfMemoryError

The method area is an area that has changed a lot during the JDK6-JDK7-JDK8 upgrade process. With the emergence of the permanent generation to remove the meta space, there have been many disputes about the method area, such as whether the runtime constant pool is in the heap or in the heap. In the meta space? There are also a series of interview questions brought by the string intern method? For these two pieces, you can refer to the article of the big brother. I chose two articles that are relatively good.

  • Is the runtime constant pool in the heap or in the metaspace? https://blog.csdn.net/weixin_44556968/article/details/109468386
  • A series of interview questions brought by the string intern method? https://blog.csdn.net/qq_36426468/article/details/110150453

Direct memory OutOfMemoryError

Insert picture description here

/**
 * -XX:MaxDirectMemorySize=10m 指定直接内存大小
 */
public class DirectMemory {
    
    
    private static final int _1mb=1024*1024;


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

        Field declaredField = Unsafe.class.getDeclaredFields()[0];
        //设置允许暴力访问
        declaredField.setAccessible(true);
        // 传入启动类加载器(启动类加载器是c++实现的,在java代码中为null)
        Unsafe unsafe = (Unsafe) declaredField.get(null);
        while (true){
    
    
            unsafe.allocateMemory(_1mb);
        }
    }
}

The memory overflow caused by direct memory has an obvious feature that there will be no obvious abnormalities in the HeapDump file. If the reader finds that the Dump file generated after the memory overflow is small and the program directly or indirectly uses DirectMemory (typically The indirect use is NIO), you can focus on the reasons for direct memory.

Guess you like

Origin blog.csdn.net/yangxiaofei_java/article/details/115272721