How to avoid memory leaks in java programs

Although jvm has a garbage collection mechanism, if the program is written without paying attention to some specific rules, it can still lead to memory leaks in java programs, which may end up with OutOfMemory exceptions.

1. Causes of memory leaks in Java

Objects in java are divided into 2 types in terms of usage, referenced and unreferenced. Garbage collection only recovers objects that are not referenced. Referenced objects, even if they are no longer used, are not recycled. Therefore, if the program has a large number of referenced and useless objects, there is a memory leak.

2. java heap memory (Heap) leak

The size of jvm heap memory is specified by two parameters -Xms and -Xmx.

2.1 Objects referenced by static members

Memory leaks are caused when large objects are referenced by static members.

Example.

private Random random = new Random();

public static final ArrayList<Double> list = new ArrayList<Double>(1000000);

for (int i = 0; i < 1000000; i++) { list.add(random.nextDouble()); }

The ArrayList is a dynamically allocated object on the heap and will normally be reclaimed by gc after use, but in this example, being referenced by the static member list, which is not reclaimed, will cause this very large ArrayList to remain in heap memory.

So you need to pay special attention to the way static members are used and avoid static members referencing large objects or objects of collection type (like ArrayList etc.).

2.2 String’s intern method

Calling String.intern() method on a large string, intern() will place the String in jvm’s memory pool (PermGen ), which is not gc. Therefore, if a large String calls the intern() method, it will generate a large amount of memory that cannot be gc, resulting in a memory leak.

If you must use the intern method with a large string, you should adjust the size of the PermGen memory with the -XX:MaxPermSize parameter.

2.3 Not closing the stream after reading it

Streams are often forgotten to be closed in development, which can lead to memory leaks. Because each stream corresponds to an open file handle at the OS level, streams that are not closed will cause the OS file handle to remain open, and jvm will consume memory to keep track of the OS open file handle. Example.

BufferedReader br = new BufferedReader(new FileReader(path));
return br.readLine();

To solve this problem, in versions of java prior to java8 you could add a close operation to the finally.

 BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br ! = null) br.close();
    }

The try-with-resources statement can be used in java8 to.

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
}

For network connections and database connections, etc. also pay attention to the closure of the connection. If a connection pool is used, then the closure operation is the responsibility of the connection pool and can be handled without the program.

2.4 Adding objects that do not implement the hashCode() and equals() methods to a HashSet

This is a simple but very common scenario. Normally a Set will filter duplicate objects, but without the hashCode() and equals() implementations, duplicate objects will keep getting added to the Set and never have a chance to be removed.

It is therefore a good programming practice to add implementations of the hashCode() and equals() methods to all classes. This can be easily done with Lombok’s @EqualsAndHashCode.

3. Methods for finding memory leaks

3.1 Logging gc logs

By specifying -verbose:gc in the jvm parameter, you can log the details of each gc, which can be used to analyze the memory usage.

3.2 Performing profiling

Memory analysis is performed through Visual VM or Java Mission Control that comes with jdk.

3.3 Code review

Discover the error codes that cause memory leak problems through code review and static code inspection.

4. Summary

Code level inspection can help find some of the memory leaks, but memory leaks in production environments are often not easy to find in advance because many of the problems only occur in large concurrency scenarios. Therefore stress testing with stress testing tools is also needed to detect potential memory leaks in advance.