Ali's two-sided original question: Which scenarios will produce OOM? How to deal with it?

This interview question was encountered by a friend during the interview. When will the OutOfMemery exception be thrown? At first glance, it seems quite simple. In fact, it is the understanding of the entire JVM that is investigated in depth, and this question can be turned into some messy answers from the Internet. In fact, in summary, basically 4 scenarios can be summarized.

Heap memory overflow

Heap memory overflow is too common, and most people should be able to think of this. Heap memory is used to store object instances. As long as we keep creating objects, and ensure that there is an accessible path between GC Roots and objects to avoid garbage collection, then This exception occurs soon after the number of objects exceeds the size limit of the maximum heap.

Write a piece of code to test and set the heap memory size to 2M.

public class HeapOOM {
    public static void main(String[] args) {
        List<HeapOOM> list = new ArrayList<>();
        while (true) {
            list.add(new HeapOOM());
        }
    }
}

Run the code, and soon you can see the OOM exception appears. The hint here is that the Java heap space overflows.

The general troubleshooting method can be analyzed by setting -XX: +HeapDumpOnOutOfMemoryError to dump the current memory dump snapshot when an exception occurs. The analysis can be analyzed using Eclipse Memory Analyzer (MAT), and independent files can be downloaded on the official website.

In addition, if you are using IDEA, you can use the commercial version of JProfiler or the open source version of JVM-Profiler. In addition, IDEA2018 has built-in analysis tools, including Flame Graph and Call Tree.

Method area (runtime constant pool) and metaspace overflow

The method area, like the heap, is an area shared by threads, including Class file information, runtime constant pool, and constant pool. The main difference between runtime constant pool and constant pool is that it is dynamic, that is, it does not have to be in the Class file. The content in the constant pool can enter the runtime constant pool, and new constants can also be put into the pool during runtime, such as the intern() method of String.

Let's write a piece of code to verify String.intern(), and we set -XX:MetaspaceSize=50m -XX:MaxMetaspaceSize=50m meta space size. Since I am using the 1.8 version of the JDK, the method area exists in the permanent generation (PermGen) before version 1.8. After 1.8, the concept of permanent generation is cancelled and converted to Metaspace. If it is the previous version, you can set PermSize MaxPermSize permanent The size of the generation. 

private static String str = "test";
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        while (true){
            String str2 = str + str;
            str = str2;
            list.add(str.intern());
        }
}

Run the code, you will find that the code reports errors.

Modify the configuration again, remove the meta space limit, modify the heap memory size -Xms20m -Xmx20m, you can see the heap memory error.

Why is this? intern() itself is a native method, its function is: if the string constant pool already contains a string equal to this String object, it will return the String object representing the string in the pool; otherwise, this String object will be included The string is added to the constant pool, and a reference to the String object is returned.

After version 1.7, the string constant pool has been transferred to the heap area, so an error of heap memory overflow will be reported. If the version is before 1.7, you will see an error of PermGen space.

Direct memory overflow

Direct memory is not part of the data area of ​​the virtual machine when it is running, and is not limited by heap memory, but is limited by the size of machine memory. For example, in NIO, native functions can be used to directly allocate off-heap memory, which easily leads to OOM problems.

The direct memory size can be specified by -XX:MaxDirectMemorySize, if not specified, the default is the same as the maximum Java heap size -Xmx.

An obvious feature of memory overflow caused by direct memory is that no obvious abnormalities will be seen in the Dump file. If the Dump file is found to be very small after OOM, and NIO is used directly or indirectly in the program, then you can consider checking it. Is it the reason for this?

Stack memory overflow

The stack is private to the thread, and its life cycle is the same as that of the thread. When each method is executed, a stack frame is created to store information such as local variable table, operand stack, dynamic link, method exit, etc. The process of method invocation is the process of stack frame pushing and popping.

In the Java virtual machine specification, two exceptions are defined for the virtual machine stack:

  1. If the stack depth requested by the thread is greater than the depth allowed by the virtual machine, a StackOverflowError exception will be thrown
  2. If the virtual machine stack can be dynamically expanded and sufficient memory cannot be applied for during expansion, an OutOfMemoryError exception will be thrown

First write a piece of code to test it, set -Xss160k, -Xss represents the stack memory size of each thread

public class StackOOM {
    private int length = 1;

    public void stackTest() {
        System.out.println("stack lenght=" + length);
        length++;
        stackTest();
    }

    public static void main(String[] args) {
        StackOOM test = new StackOOM();
        test.stackTest();
    }
}

The test found that no matter how to set the parameters in a single thread, StackOverflow is abnormal.

Try to modify the code to be multi-threaded and adjust -Xss2m, because the larger the memory allocated for each thread, the smaller the number of threads that can be accommodated in the stack space, and the easier it is to cause memory overflow. Conversely, if the memory is insufficient, this parameter can be reduced to achieve the purpose of supporting more threads.

public class StackOOM {
    private void dontStop() {
        while (true) {
        }
    }

    public void stackLeakByThread() {
        while (true) {
            new Thread(() -> dontStop()).start();
        }
    }

    public static void main(String[] args) throws Throwable {
        StackOOM stackOOM = new StackOOM();
        stackOOM.stackLeakByThread();
    }
}

If you think this article is helpful to you, you can like it and follow it to support it, or you can follow my public account, there are more technical dry goods articles and related information sharing, everyone can learn and progress together!

 

Guess you like

Origin blog.csdn.net/weixin_50205273/article/details/108650432