Categories
java memory memory-leaks

How can I create a memory leak in Java?

3605

I just had an interview where I was asked to create a memory leak with Java.

Needless to say, I felt pretty dumb having no clue on how to even start creating one.

What would an example be?

3

  • 35

    Ironically, the harder question for every non-trivial Java program is how not to create a memory leak!

    May 12, 2021 at 15:00

  • 3

    Keep adding new objects to a container, but forget to add the code that removes them or implement partially working code that doesn’t clean up all of them as the program progresses.

    – Galik

    Jul 23, 2021 at 4:58

  • 3

    The most common memory leaks in Java server systems are in shared state — caches and services which are shared between requests. Many of the answers here seem to be over-complex, ignoring this obvious and common area. One rather common leak pattern is probably an application-scoped Map with request-scoped keys (eg, some kind of hand-rolled cache).

    – Thomas W

    Feb 3 at 2:37


2533

+50

Here’s a good way to create a true memory leak (objects inaccessible by running code but still stored in memory) in pure Java:

  1. The application creates a long-running thread (or use a thread pool to leak even faster).
  2. The thread loads a class via an (optionally custom) ClassLoader.
  3. The class allocates a large chunk of memory (e.g. new byte[1000000]), stores a strong reference to it in a static field, and then stores a reference to itself in a ThreadLocal. Allocating the extra memory is optional (leaking the class instance is enough), but it will make the leak work that much faster.
  4. The application clears all references to the custom class or the ClassLoader it was loaded from.
  5. Repeat.

Due to the way ThreadLocal is implemented in Oracle’s JDK, this creates a memory leak:

  • Each Thread has a private field threadLocals, which actually stores the thread-local values.
  • Each key in this map is a weak reference to a ThreadLocal object, so after that ThreadLocal object is garbage-collected, its entry is removed from the map.
  • But each value is a strong reference, so when a value (directly or indirectly) points to the ThreadLocal object that is its key, that object will neither be garbage-collected nor removed from the map as long as the thread lives.

In this example, the chain of strong references looks like this:

Thread object → threadLocals map → instance of example class → example class → static ThreadLocal field → ThreadLocal object.

(The ClassLoader doesn’t really play a role in creating the leak, it just makes the leak worse because of this additional reference chain: example class → ClassLoader → all the classes it has loaded. It was even worse in many JVM implementations, especially prior to Java 7, because classes and ClassLoaders were allocated straight into permgen and were never garbage-collected at all.)

A variation on this pattern is why application containers (like Tomcat) can leak memory like a sieve if you frequently redeploy applications which happen to use ThreadLocals that in some way point back to themselves. This can happen for a number of subtle reasons and is often hard to debug and/or fix.

Update: Since lots of people keep asking for it, here’s some example code that shows this behavior in action.

2

  • 220

    +1 ClassLoader leaks are some of the most commonly painful memory leaks in the JEE world, often caused by 3rd party libs that transform data (BeanUtils, XML/JSON codecs). This can happen when the lib is loaded outside your application’s root classloader but holds references to your classes (eg. by caching). When you undeploy/redeploy your app the JVM is unable to garbage collect the app’s classloader (and therefore all classes loaded by it), so with repeat deploys the app server eventually borks. If lucky you get a clue with ClassCastException z.x.y.Abc cannot be cast to z.x.y.Abc

    – earcam

    Jun 27, 2011 at 16:55

  • 66

    +1: Classloader leaks are a nightmare. I spent weeks trying to figure them out. The sad thing is, as what @earcam has said, they are mostly caused by 3rd party libs and also most profilers can’t detect these leaks. There’s a good and clear explanation on this blog about Classloader leaks. blogs.oracle.com/fkieviet/entry/…

    – Adrian M

    Jul 8, 2011 at 9:08

1317

+50

Static field holding an object reference [especially a final field]

class MemorableClass {
    static final ArrayList list = new ArrayList(100);
}

Calling String.intern() on a lengthy string

String str = readString(); // read lengthy string any source db,textbox/jsp etc..
// This will place the string in memory pool from which you can't remove
str.intern();

(Unclosed) open streams (file , network, etc.)

try {
    BufferedReader br = new BufferedReader(new FileReader(inputFile));
    ...
    ...
} catch (Exception e) {
    e.printStackTrace();
}

Unclosed connections

try {
    Connection conn = ConnectionFactory.getConnection();
    ...
    ...
} catch (Exception e) {
    e.printStackTrace();
}

Areas that are unreachable from JVM’s garbage collector, such as memory allocated through native methods.

In web applications, some objects are stored in application scope until the application is explicitly stopped or removed.

getServletContext().setAttribute("SOME_MAP", map);

Incorrect or inappropriate JVM options, such as the noclassgc option on IBM JDK that prevents unused class garbage collection

See IBM JDK settings.

3

  • 211

    I’d disagree that context and session attributes are “leaks.” They’re just long-lived variables. And the static final field is more or less just a constant. Maybe large constants should be avoided, but I don’t think it’s fair to call it a memory leak.

    Jul 13, 2011 at 4:08

  • 98

    (Unclosed) open streams ( file , network etc… ), doesn’t leak for real, during finalization (which will be after the next GC cycle) close() is going to be scheduled (close() is usually not invoked in the finalizer thread since might be a blocking operation). It’s a bad practice not to close, but it doesn’t cause a leak. Unclosed java.sql.Connection is the same.

    – bestsss

    Jul 17, 2011 at 18:38


  • 41

    In most sane JVMs, it appears as though the String class only has a weak reference on its intern hashtable contents. As such, it is garbage collected properly and not a leak. (but IANAJP) mindprod.com/jgloss/interned.html#GC

    – mbauman

    Jul 22, 2011 at 1:32

497

+50

A simple thing to do is to use a HashSet with an incorrect (or non-existent) hashCode() or equals(), and then keep adding “duplicates”. Instead of ignoring duplicates as it should, the set will only ever grow and you won’t be able to remove them.

If you want these bad keys/elements to hang around you can use a static field like

class BadKey {
   // no hashCode or equals();
   public final String key;
   public BadKey(String key) { this.key = key; }
}

Map map = System.getProperties();
map.put(new BadKey("key"), "value"); // Memory leak even if your threads die.

5

  • 86

    Actually, you can remove the elements from a HashSet even if the element class gets hashCode and equals wrong; just get an iterator for the set and use its remove method, as the iterator actually operates on the underlying entries themselves and not the elements. (Note that an unimplemented hashCode/equals is not enough to trigger a leak; the defaults implement simple object identity and so you can get the elements and remove them normally.)

    Jun 25, 2011 at 17:23

  • 2

    @Donal your last statement is incorrect. You can’t get the element because you have no reference to it. The only reference to the key put in the map is the one that the map itself holds. Only if you went BadKey myKey = new BadKey("key"); map.put(key,"value"); would you ever be able to get it out. You are correct you could use the iterator to remove it, but you cannot always do that with all data structures. For example, if you don’t null out the reference in @meriton’s answer, that will be lost forever. You have no access to the backing array, and iterator will stop short of it.

    – corsiKa

    Jun 26, 2011 at 0:13

  • 1

    The only way to remove elements when equals/hashCode is incorrect is to use Iterator.remove() when you find a match using a different method of comparison.

    Jun 26, 2011 at 7:14

  • 14

    @Donal what I’m trying to say, I guess, is I disagree with your definition of a memory leak. I would consider (to continue the analogy) your iterator-removal technique to be a drip-pan under a leak; the leak still exists regardless of the drip pan.

    – corsiKa

    Jun 26, 2011 at 20:24

  • 110

    I agree, this is not a memory “leak”, because you can just remove references to the hashset and wait for the GC to kick in, and presto! the memory goes back.

    Jul 2, 2011 at 4:30