Java Dailly Tip: How to create memory leak in Java

Java Dailly Tip: How to create memory leak in Java

Problem

I'm just after the interview. I was supposed to show a memory leak in Java but I couldn't create it. How to do it?

Solution

Here's a nice 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 uses a thread pool to leak even faster).
  2. The thread loads the class via the (optionally custom) ClassLoader.
  3. The class allocates a large chunk of memory (eg a new byte [10]), stores a strong reference to it in a static field, and then stores the reference to itself in ThreadLocal. Allocating additional memory is optional (the leak from an instance of the class will suffice), but it will make the leak run much faster.
  4. The application clears all references to the custom class or ClassLoader from which it was loaded. To repeat.
  5. Due to the way in which T.

ThreadLocal is implemented in Oracle JDK, it causes a memory leak:

  • Each thread has a private threadLocals field that actually holds thread local values.
  • Each key in this map is a weak reference to a ThreadLocal object, so after that ThreadLocal object is cleared, its entry is removed from the map.
  • But each value is a strong reference, so when the value (directly or indirectly) points to a ThreadLocal object that is its key, that object will not be picked up or removed from the map as long as the thread is alive.
  • In this example, the strong reference chain looks like this:Thread object → threadLocals map → example class instance → sample class → static ThreadLocal field → ThreadLocal object. (ClassLoader doesn't really play a role in creating a leak, it just makes it worse because of an extra string of references: example class → ClassLoader → all the classes it has loaded.

Let's see some examples:

  1. Static field holding an object reference [especially a final field]
    class MemorableClass {
        static final ArrayList list = new ArrayList(100);
    }
    
  2. 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();
    }
    
  3. Unclosed connections
    try {
        Connection conn = ConnectionFactory.getConnection();
        ...
        ...
    } catch (Exception e) {
        e.printStacktrace();
    }
    
  4. 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");
    
    

Share this Post