In Java, there are two main types of memory-related problems, memory overflow and memory leak.

  • Out Of Memory: When the JVM does not have enough Memory to apply for Memory. You go to a hole and you find it full.
  • A Memory Leak is when Memory is requested, but not released, resulting in a waste of Memory space. In common parlance, someone is a dog in the manger.

1. Memory overflow

Several of the JVM’s memory regions have the potential to run out of memory (OOM) exceptions except for the program counter.

1.1. Java heap overflow

The Java heap is used to store object instances, and as long as we keep creating objects and making sure that there is a reachable path between the GC Roots and the objects to avoid the garbage collection mechanism to clean them up, we will run out of memory exceptions as the number of objects increases and the total capacity reaches the maximum heap capacity limit.

Let’s look at an example of code:

/ * * * VM parameters: - Xms20m - Xmx20m - XX: + HeapDumpOnOutOfMemoryError * /
public class HeapOOM {
    static class OOMObject {}public static void main(String[] args) {
        List<OOMObject> list = new ArrayList<OOMObject>();
        while (true) {
            list.add(newOOMObject()); }}}Copy the code

Next, let’s set the JVM parameters when the program starts. The memory size is limited to 20 MB and cannot be expanded. Run -xx: +HeapDumpOnOutOf -memoryError to Dump heap Dump snapshots from the VM.

Set JVM startup parameters in Idea as shown below:

Run it:

OutOfMemoryError of Java heap memory is the most common out-of-memory exception in practical applications. Java heap memory leak, “Java. Lang. OutOfMemoryError exception stack information will follow further prompt” Java heap space “. The Java heap file snapshot file is dumped to the java_pid18728.hprof file.

To resolve this Memory area exception, the conventional approach is to first analyze the heap Dump snapshot dumped by a Memory image analysis tool (such as JProfiler, Eclipse Memory Analyzer, etc.).

The memory usage is as follows:

You can then view the code problem as follows:

Common heap JVM parameters:

-xx :PrintFlagsFinal :PrintFlagsFinal :PrintFlagsFinal :PrintFlagsFinal :PrintFlagsFinal :PrintFlagsFinal :PrintFlagsFinal :PrintFlagsFinal :PrintFlagsFinal :PrintFlagsFinal Maximum heap size memory (default: 1/4 of physical memory) -xmn: Set new generation size (initial and maximum) -xx :NewRatio: -xx :SurvivorRatio: set the ratio of Eden and S0/S1 Spaces in the new generation. -xx :MaxTenuringThreshold: The biggest age 15) (the default setting new generation garbage – XX: + PrintGCDetails: output detailed GC log printed the brief information of GC: (1) – XX: + PrintGC (2) – verbose: GC – XX: HandlePromotionFailure: Whether to set space allocation guarantee

1.2. Virtual machine stack and local method stack overflow

The HotSpot virtual machine combines the virtual machine stack with the local method stack, so for HotSpot, the -xoss parameter (setting the local method stack size) exists but has no effect, and the stack size can only be set by the -xss parameter. There are two exceptions to the virtual machine stack and the local method stack:

  • StackOverflowError is thrown if the stack depth requested by the thread is greater than the maximum depth allowed by the virtual machine.

  • If the virtual machine’s stack memory allows dynamic scaling, an OutOfMemoryError is raised when sufficient memory cannot be allocated to the extended stack.

1.2.1, StackOverflowError

HotSpot virtual machines do not support dynamic stack scaling and in HotSpot virtual machines StackOverflowError can result in either of the following situations.

  • The stack capacity is too small. Procedure

    Below, use the Xss parameter to reduce stack memory capacity

/** * VM parameters: -xss128K */
public class JavaVMStackSOF {
    private int stackLength = 1;

    public void stackLeak(a) {
        stackLength++;
        stackLeak();
    }

    public static void main(String[] args) throws Throwable {
        JavaVMStackSOF oom = new JavaVMStackSOF();
        try {
            oom.stackLeak();
        } catch (Throwable e) {
            System.out.println("stack length:" + oom.stackLength);
            throwe; }}}Copy the code

Running results:

  • The stack frame is too big

    As follows, a long list of variables takes up space in the local variable table.

Running results:

Either because the stack frame is too large or the virtual machine stack size is too small, the HotSpot virtual machine throws StackOverflowError when the memory of the new stack frame cannot be allocated.

1.2.2, OutOfMemoryError

Dynamic stack extensions are not supported, but memory overflow exceptions can be generated on HotSpot by continually creating threads.

It is important to note that there is no direct relationship between overflow exceptions and sufficient stack space, and it depends on the memory usage of the operating system itself. Because the operating system has a limited amount of memory for each process, the number of threads will naturally exceed the capacity of the process.

Memory overflow exception caused by thread creation:

/** * VM parameters: -xss2m */
public class JavaVMStackOOM {
    private void dontStop(a) {
        while (true) {}}public void stackLeakByThread(a) {
        while (true) {
            Thread thread = new Thread(new Runnable() {
                public void run(a) { dontStop(); }}); thread.start(); }}public static void main(String[] args) throws Throwable {
        JavaVMStackOOM oom = newJavaVMStackOOM(); oom.stackLeakByThread(); }}Copy the code

The above is a relatively risky code, may cause the system to suspend operation, the result is as follows:

1.3. Method area and runtime constant pool overflow

In JDK1.7, the string constant pool was moved to the heap. In JDK1.8, a region meta-space was created in direct memory to implement square regions.

String: Intern () is a local method that returns a reference to a String representing the String in the pool if the String constant pool already contains a String equal to the String. Otherwise, the String contained in this String is added to the constant pool and a reference to this String is returned. In the HotSpot virtual machine of JDK 6 or earlier, the constant pool is allocated in the persistent generation, which has unlimited memory and can cause errors:

java.lang.OutOfMemoryError: PermGen space
Copy the code

1.4. Native direct memory overflow

The size of DirectMemory can be specified with the -xx: MaxDirectMemorySize parameter. If not specified, it defaults to the maximum Java heap size (specified by -xmx).

Retrieving an Unsafe instance directly uses reflection to request memory allocation from the operating system:

/** * VM parameters: -xmx20m-xx :MaxDirectMemorySize=10M */
public class DirectMemoryOOM {
    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) throws Exception {
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe) unsafeField.get(null);
        while (true) { unsafe.allocateMemory(_1MB); }}}Copy the code

Running results:

An obvious feature of a direct memory overflow is that there is no obvious exception to be seen in the Heap Dump file.

2. Memory leaks

Memory reclamation is simply an object that should be garbage collected but is not garbage collected.

In the figure above: Object X refers to object Y, and X has a longer life than Y. At the end of Y’s life, the garbage collector does not reclaim object Y.

Let’s look at some examples of memory leaks:

  • Static collection classes cause memory leaks

    Static collections have the same lifecycle as the JVM, so objects referenced by static collections cannot be freed.

public class OOM {
 static List list = new ArrayList();

 public void oomTests(a){
   Object obj = newObject(); list.add(obj); }}Copy the code
  • Singleton mode:

    Similar to the above example, singletons exist as static variables for the entire life of the JVM after initialization. If a singleton holds an external reference, the external object cannot be collected by the GC, resulting in a memory leak.

  • Data connection, IO, Socket connection

    If a Connection is not used any more, you need to call the close method to close the Connection. Only after the Connection is closed, the GC will reclaim the corresponding object (Connection, Statement, ResultSet, Session). Forgetting to turn these resources off can result in persistent memory usage that cannot be collected by the GC.

        try {
            Connection conn = null;
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("url".""."");
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("...");
          } catch (Exception e) { 
           
          }finally {
            // Do not close the connection}}Copy the code
  • Unreasonable scope of a variable

    A variable’s definition scope is larger than its use scope, and memory leaks are likely; Not setting the object to NULL in time is likely to cause a memory leak.

public class Simple {
    Object object;
    public void method1(a){
        object = new Object();
        / /... Other code
        // For scoping reasons, the memory allocated by object objects is not freed immediately after execution of method1
        object = null; }}Copy the code
  • A non-static inner class that references an outer class

    Initialization of a non-static inner class (or anonymous class) always depends on an instance of the outer class. By default, every non-static inner class contains an implicit reference to its containing class. If used in a program, this inner class object is not recycled even after the containing class object is out of scope. (The inner class object implicitly holds a reference to the outer class object, making it unrecyclable.)

  • The Hash value changed. Procedure

    The Hash value of an object changes. If a container such as HashMap or HashSet is used, the modified Hah value of the object is different from the Hash value stored in the container. As a result, the current object cannot be deleted from the container, resulting in memory leakage.

  • Memory leak caused by ThreadLocal

    ThreadLocal can achieve thread-isolation of variables, but if used incorrectly, it can introduce memory leaks.



Reference:

[1] : Zhou Zhipeng, In Depth Understanding the Java Virtual Machine: Advanced JVM Features and Best Practices

[2] : Zhou Zhipeng et al translated Java Virtual Machine Specification

[3] : Feng Yafei, “Revealing the Design Principle and Implementation of Java Virtual Machine JVM”

[4] : What are memory spills and leaks in Java? Let me give you an interesting example

[5] : The little white has not understood the memory overflow, can only use the case to say to him

[6] : Intellij IDEA integrated JProfiler performance analysis artifact

[7] : JVM series (ii) – JVM memory area details

[8] 1 article to clarify 8 JVM memory overflow (OOM) causes and solutions

[9] : How many of the ten JVM memory overflow situations have you encountered?

[10] : JVM series (II) : JVM memory leakage, memory overflow and troubleshooting