Fundamentals 5 min read

5 Common Java Memory Leak Scenarios and How to Detect Them

This article explains five common Java memory leak scenarios—long-lived collections, unclosed resources, ThreadLocal misuse, inner class references, and unregistered listeners—with code examples and recommends MAT and VisualVM for leak detection.

Java Captain
Java Captain
Java Captain
5 Common Java Memory Leak Scenarios and How to Detect Them

Memory leak refers to objects that are no longer used by the program but cannot be reclaimed by the garbage collector, occupying memory long-term and potentially causing OOM (OutOfMemoryError).

1. Long-Lived Collections

Placing objects into static or long-lived collections (e.g.,

public static List<Object> list = new ArrayList<>();

) keeps references alive even after the objects are no longer needed, preventing GC.

2. Unclosed Resources

Connections, streams, and sockets not closed via close() occupy both memory and system resources such as file handles and network connections. Examples include database connections, FileInputStream, and Socket connections.

public class FileTest {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("test.txt");
            // Read file, fis.close() not called
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            // fis.close() not called → fis holds native reference, cannot be reclaimed
        }
    }
}

3. ThreadLocal Misuse

Storing objects in ThreadLocal without calling remove() afterward. If the thread comes from a thread pool (which reuses threads), the values in the thread's ThreadLocalMap persist indefinitely.

public class ThreadLocalTest {
    private static ThreadLocal<User> userThreadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        // Thread pool (core threads live long)
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 4, 10, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            new ThreadFactoryBuilder().setNameFormat("my-thread-pool-%d").setDaemon(false).setPriority(Thread.NORM_PRIORITY).build(),
            new ThreadPoolExecutor.AbortPolicy()
        );

        executor.submit(() -> {
            User user = new User("李四", 30);
            userThreadLocal.set(user); // Store in ThreadLocal
            // Business logic finishes, remove() not called
            // Core thread not destroyed, ThreadLocal still holds user reference
        });
    }
}

Note: Missing remove() can also cause ThreadLocal value cross-talk between tasks.

4. Inner Class Holding Outer Class Reference

Non-static inner classes (or anonymous classes) implicitly hold a reference to the outer class . If the inner class instance lives longer (e.g., cached or referenced by another thread), it prevents the outer class from being collected.

public class OuterClass {
    private byte[] bigData = new byte[1024 * 1024 * 10]; // 10MB large object

    // Non-static inner class
    class InnerClass {
        // Inner class implicitly holds OuterClass reference
    }

    public InnerClass createInner() {
        return new InnerClass();
    }

    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        InnerClass inner = outer.createInner();

        // Null outer reference, but inner still holds outer reference
        outer = null;

        // If inner is held by static variable/thread long-term → outer object (including bigData) cannot be reclaimed
    }
}

5. Listeners and Callbacks

Registering listeners or callbacks without unregistering them when the object is no longer needed causes the source object to retain references to the listeners (e.g., event listeners, message queue consumers).

Troubleshooting Tools

MAT (Memory Analyzer Tool): Analyzes heap dump files to locate leaking objects and reference chains (who holds the leaking object).

VisualVM: Bundled with JDK, monitors memory usage trends, generates heap dumps, and enables basic leak investigation.

The author lists only common scenarios and invites readers to supplement other memory leak cases.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaGarbage CollectionMemory LeakThreadLocalMATVisualVMResource LeakInner Class
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.