Summary of 8 cases of memory leakage in java

Summary of 8 cases of memory leakage in java

Since the java JVM introduces the garbage collection mechanism, the garbage collector will automatically collect objects that are no longer used. Those who understand the JVM collection mechanism know that the JVM uses the reference counting method and reachability analysis algorithm to judge whether an object is no longer used. In essence, it is to judge whether an object is still referenced. In this case, due to different code implementations, many kinds of memory leakage problems will occur (causing the JVM to mistakenly assume that this object is still referenced and cannot be recycled, resulting in memory leakage).

1. Static collection class

Such as HashMap, LinkedList, etc. If these containers are static, their life cycle is consistent with the program, and the objects in the container will not be released before the end of the program, resulting in memory leakage. In short, long-lived objects hold references to short-lived objects. Although short-lived objects are no longer used, they cannot be recycled because long-lived objects hold their references.

2. Various connections, such as database connection, network connection, IO connection, etc

In the process of operating the database, you first need to establish a Connection with the database. When it is no longer used, you need to call the close method to release the Connection with the database. Only after the Connection is closed will the garbage collector recycle the corresponding object. Otherwise, if the Connection, Statement or ResultSet is not explicitly closed during database access, a large number of objects will not be recycled, resulting in memory leakage.

3. Unreasonable scope of variable

Generally speaking, the scope of a variable definition is larger than its use scope, which is likely to cause memory leakage. On the other hand, if the object is not set to null in time, it is likely to lead to memory leakage.

 public class UsingRandom {		
	private String msg;
	public void receiveMsg(){
		readFromNet();// Accept data from the network and save it to msg
		saveDB();// Save msg to database
	}
}

If the pseudo code is stored in the readFromNet, the received message is saved in the variable msg, then the saveDB method is used to save the contents of msg to the database. At this time, msg is useless. Because the life cycle of msg is the same as the life cycle of the object, msg can not be recovered at the same time, resulting in memory leakage.

In fact, this msg variable can be placed inside the receiveMsg method. When the method is used up, the MSG life cycle will end and can be recycled at this time. Another method is to set msg to null after using MSG, so that the garbage collector will also recycle the memory space of MSG.

4. Internal class holds external class

If the method of an instance object of an external class returns an instance object of an internal class, the internal class object has been referenced for a long time. Even if the external class instance object is no longer used, because the internal class holds the instance object of the external class, the external class object will not be garbage collected, which will also cause memory leakage.

5. Change hash value

After an object is stored in the HashSet set, the fields in the object that participate in the calculation of hash value cannot be modified. Otherwise, the modified hash value of the object is different from the hash value originally stored in the HashSet set. In this case, even if the contains method uses the current reference of the object as a parameter to retrieve the object in the HashSet set, It will also return the result that the object cannot be found, which will also lead to the failure to delete the current object separately from the HashSet collection, resulting in memory leakage

6. For example - see if you can find a memory leak

import java.util.Arrays;

public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
        return elements[--size];
    }

    private void ensureCapacity() {
        if (elements.length == size)
            elements = Arrays.copyOf(elements, 2 * size + 1);
    }
}

6.1 cause analysis

There is no obvious error in the above program, but there is a memory leak in this program. With the increase of GC activity or the continuous increase of memory occupation, the decline of program performance will be shown. In serious cases, it can lead to memory leak, but this failure is relatively rare.
The main problem of the code is in the pop function, which is shown in this figure below
Suppose that the stack keeps growing, as shown in the figure below

When a large number of pop operations are performed, the gc will not be released because the reference is not empty, as shown in the following figure

It can be seen from the above figure that if the stack grows first and shrinks, the objects popped from the stack will not be recycled as garbage. Even if the program no longer uses these queue images in the stack, they will not be recycled, because the reference of this object is still saved in the stack, commonly known as expired reference. This memory leak is very hidden.

1.2 solutions

public Object pop() {
    if (size == 0)
    throw new EmptyStackException();
    Object result = elements[--size];
    elements[size] = null;
    return result;
}

Once references expire, empty them and leave them empty.

7. Cache leakage

Another common source of memory leakage is the cache. Once you put an object reference into the cache, it is easy to forget. For this problem, you can use WeakHashMap to represent the cache. The feature of this map is that when the key has no other reference except its own reference to the key, the map will automatically discard this value

7.1 code example

package com.ratel.test;

/**
 * @Business description:
 * @package_name:  com.ratel.test
 * @project_name:  ssm
 * @author:  ratelfu@qq.com
 * @create_time:  2019-04-18 20:20
 * @copyright (c) ratelfu copyright
 */
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.TimeUnit;

public class MapTest {
    static Map wMap = new WeakHashMap();
    static Map map = new HashMap();
    public static void main(String[] args) {
        init();
        testWeakHashMap();
        testHashMap();
    }

      public static void init(){
            String ref1= new String("obejct1");
            String ref2 = new String("obejct2");
            String ref3 = new String ("obejct3");
            String ref4 = new String ("obejct4");
            wMap.put(ref1, "chaheObject1");
            wMap.put(ref2, "chaheObject2");
            map.put(ref3, "chaheObject3");
            map.put(ref4, "chaheObject4");
            System.out.println("String quote ref1,ref2,ref3,ref4 disappear");
	}

    public static void testWeakHashMap(){

        System.out.println("WeakHashMap GC before");
        for (Object o : wMap.entrySet()) {
            System.out.println(o);
        }
        try {
            System.gc();
            TimeUnit.SECONDS.sleep(20);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("WeakHashMap GC after");
        for (Object o : wMap.entrySet()) {
            System.out.println(o);
        }
    }
    public static void testHashMap(){
        System.out.println("HashMap GC before");
        for (Object o : map.entrySet()) {
            System.out.println(o);
        }
        try {
            System.gc();
            TimeUnit.SECONDS.sleep(20);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("HashMap GC after");
        for (Object o : map.entrySet()) {
            System.out.println(o);
        }
    }

}
/** result
 String References ref1, ref2, ref3, ref4 disappear
 WeakHashMap GC before
 obejct2=chaheObject2
 obejct1=chaheObject1
 WeakHashMap GC after
 HashMap GC before
 obejct4=chaheObject4
 obejct3=chaheObject3
 Disconnected from the target VM, address: '127.0.0.1:51628', transport: 'socket'
 HashMap GC after
 obejct4=chaheObject4
 obejct3=chaheObject3
 **/


The above code and figure show how the weakhashmap automatically releases the cache object. When the init function is completed, the local variable string references weakd1, weakd2, D1 and D2 will disappear. At this time, only the reference to the string object saved in the static map. You can see that after calling gc, the hashmap is not recycled, but the cache in the weakhashmap is recycled.

8. Listener and callback

The third common source of memory leaks is listeners and other callbacks. If the client registers callbacks in the API you implement, but does not display cancellations, it will accumulate. The best way to ensure that callbacks are immediately garbage collected is to save only their if references, for example, as keys in the WeakHashMap.

Keywords: Java Database jvm Multithreading Memory Leak

Added by Joseph Sliker on Wed, 22 Dec 2021 01:42:10 +0200