SoFunction
Updated on 2025-04-14

10 Best Practices to Avoid Java Memory Leakage

introduction

As a widely used programming language, Java's automatic memory management mechanism (garbage collection) reduces the burden of manual memory management for developers. However, even with the help of the garbage collector, Java applications may still experience memory leak problems. Memory leaks not only cause application performance to decline, but may also raise an OutOfMemoryError exception, causing the application to completely crash.

This article will introduce 10 best practices to avoid Java memory leaks to help developers build more robust and efficient Java applications.

What is a Java memory leak

In Java, memory leaks refer to objects that are no longer used in the program that cannot be recycled by the garbage collector. These objects will always occupy memory space, which will eventually lead to a decrease in available memory or even exhaustion.

Unlike memory leaks in C/C++ that are not freed, memory leaks in Java are usually due to the still exist references to useless objects, which makes the garbage collector unable to recognize and recycle these objects.

10 Best Practices to Avoid Java Memory Leaks

1. Close resources in a timely manner

Unclosed resources (such as files, database connections, network connections, etc.) are one of the most common sources of memory leaks in Java.

// Not recommendedpublic void readFile(String path) throws IOException {
    FileInputStream fis = new FileInputStream(path);
    // Use fis to read the file    // If an exception occurs here, fis may not be closed}

// Recommended method: use try-with-resourcespublic void readFile(String path) throws IOException {
    try (FileInputStream fis = new FileInputStream(path)) {
        // Use fis to read the file    } // Fis will be automatically closed, even if an exception occurs}

2. Pay attention to static collection classes

The life cycle of static collection classes (such as HashMap, ArrayList, etc.) is the same as that of the application. If objects are constantly added to them without removing them, it will cause memory leaks.

public class CacheManager {
    // Static collections may cause memory leaks    private static final Map<String, Object> cache = new HashMap<>();
    
    public static void addToCache(String key, Object value) {
        (key, value);
    }
    
    // Ensure a cleanup mechanism is provided    public static void removeFromCache(String key) {
        (key);
    }
    
    public static void clearCache() {
        ();
    }
}

3. Avoid internal classes holding external class references

Non-static inner classes implicitly hold references to external classes. If the instance of the inner class has a longer life cycle than the instance of the outer class, it may cause the external class to be garbage collected.

public class Outer {
    private byte[] data = new byte[100000]; // Big object    
    // Not recommended: non-static internal classes    public class Inner {
        public void process() {
            ();
        }
    }
    
    // Recommended: Static internal class    public static class StaticInner {
        private final Outer outer;
        
        public StaticInner(Outer outer) {
             = outer;
        }
        
        public void process() {
            ();
        }
    }
}

4. Correctly implement equals() and hashCode() methods

When using hash-based collection classes such as HashMap and HashSet, if the equals() and hashCode() methods are not implemented correctly, duplicate objects may not be recognized, resulting in memory leaks.

public class Person {
    private String name;
    private int age;
    
    // constructor, getter and setter omitted    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != ()) return false;
        Person person = (Person) o;
        return age ==  && (name, );
    }
    
    @Override
    public int hashCode() {
        return (name, age);
    }
}

5. Use WeakReference and SoftReference

WeakReference or SoftReference can be used when you need to cache objects but don't want to block garbage collection.

public class ImageCache {
    // Using WeakHashMap, when the key is no longer referenced, the corresponding entry will be automatically removed    private final Map<String, WeakReference<BufferedImage>> cache = new WeakHashMap<>();
    
    public BufferedImage getImage(String path) {
        WeakReference<BufferedImage> reference = (path);
        BufferedImage image = (reference != null) ? () : null;
        
        if (image == null) {
            image = loadImage(path);
            (path, new WeakReference<>(image));
        }
        
        return image;
    }
    
    private BufferedImage loadImage(String path) {
        // Code to load the picture        return null; // Return the loaded picture in actual application    }
}

6. Avoid Finalizer

Java's finalizer (finalize() method) performs unpredictable, which may cause objects to stay in memory longer than they need.

// Not recommendedpublic class ResourceHolder {
    private FileInputStream fis;
    
    public ResourceHolder(String path) throws IOException {
        fis = new FileInputStream(path);
    }
    
    @Override
    protected void finalize() throws Throwable {
        if (fis != null) {
            ();
        }
        ();
    }
}

​​​​​​​// Recommended: Implement the AutoCloseable interfacepublic class ResourceHolder implements AutoCloseable {
    private FileInputStream fis;
    
    public ResourceHolder(String path) throws IOException {
        fis = new FileInputStream(path);
    }
    
    @Override
    public void close() throws IOException {
        if (fis != null) {
            ();
            fis = null;
        }
    }
}

7. Pay attention to the use of ThreadLocal

ThreadLocal variables that are not cleaned correctly can cause memory leaks, especially when using thread pools.

public class ThreadLocalExample {
    // Define ThreadLocal variable    private static final ThreadLocal<byte[]> threadLocalBuffer = 
        (() -> new byte[1024 * 1024]); // 1MB buffer
    
    public void process() {
        // Use ThreadLocal variable        byte[] buffer = ();
        // Processing logic...        
        // Important: Clean up the ThreadLocal variable after use        ();
    }
}

8. Avoid circular references

References may cause objects to be garbage collected. When designing relationships between classes, unnecessary two-way references should be avoided, or weak references should be used to break the loop.

// Potential problem: Parent and Child reference each otherpublic class Parent {
    private List<Child> children = new ArrayList<>();
    
    public void addChild(Child child) {
        (child);
        (this);
    }
}

public class Child {
    private Parent parent;
    
    public void setParent(Parent parent) {
         = parent;
    }
}

​​​​​​​// Solution: Use weak references to break the looppublic class Child {
    private WeakReference<Parent> parentRef;
    
    public void setParent(Parent parent) {
         = new WeakReference<>(parent);
    }
    
    public Parent getParent() {
        return (parentRef != null) ? () : null;
    }
}

9. Use appropriate caching policies

Cache is a common source of memory leaks, and appropriate caching strategies should be used, such as setting cache size limits, expiration time, etc.

// Use Guava Cache library to implement cache with size limit and expiration timeLoadingCache<Key, Value> cache = ()
    .maximumSize(1000) // Up to 1000 entries are cached    .expireAfterWrite(10, ) // Expired after 10 minutes of writing    .removalListener(notification -> {
        // Optional: Process the removed entries        ("Removed: " + () + " due to " + ());
    })
    .build(new CacheLoader<Key, Value>() {
        @Override
        public Value load(Key key) throws Exception {
            //Loading the data logic            return null; // Return the loaded value in actual application        }
    });

10. Check regularly using memory analysis tools

Regularly use memory analysis tools (such as Java VisualVM, Eclipse Memory Analyzer, etc.) to check the memory usage of your application, and detect and resolve memory leaks early.

// Manually trigger garbage collection and memory usage printing at key points (for development and debugging only)();
Runtime runtime = ();
long usedMemory = () - ();
("Used Memory: " + (usedMemory / 1024 / 1024) + " MB");

How to detect Java memory leaks

In addition to the best practices mentioned above, it is also important to know how to detect memory leaks:

Parameter monitoring: Use the -XX:+HeapDumpOnOutOfMemoryError parameter to automatically generate a heap dump file when OOM occurs.

2. Use professional tools:

  • Java VisualVM
  • Eclipse Memory Analyzer (MAT)
  • YourKit Java Profiler
  • JProfiler

3. Heap dump analysis: Generate heap dump files regularly and analyze object reference relationships.

4. Memory usage trend monitoring: Observe the memory usage trend after the application runs for a long time. Stable growth may mean memory leaks.

in conclusion

Although memory leak problem is not as common as languages ​​such as C/C++ in Java applications, it still needs to be paid enough attention. By following the 10 best practices introduced in this article, developers can effectively reduce the risk of memory leaks in Java applications and improve application stability and performance.

The above are the detailed contents of 10 best practices to avoid Java memory leaks. For more information about Java avoiding memory leaks, please pay attention to my other related articles!