Splunk Completes Acquisition of Plumbr Learn more

To blog |

How to create a memory leak

April 23, 2013 by Ivo Mägi Filed under: Memory Leaks

This is going to be a rather evil post – something you will be googling when you really wish to make someone’s life a misery. In the world of Java development memory leaks are just the type of bugs you would introduce in this case. Days or even weeks of sleepless nights in the office are guaranteed for your victim.

We will describe two leaks in this post. Both of them are easy to understand and reproduce. The leaks originate from the real world case studies, but for the sake of clarity we have extracted the demo cases to be shorter and simpler for you to grasp.

But rest assured – after we have both seen and fixed hundreds of leaks – cases similar to those demoed this are more common than you might have expected.

First contestant to step into the ring – the infamous HashSet/HashMap solutions where the key used either does not have or has an incorrect equals()/hashCode() solutions.

class KeylessEntry {
 
   static class Key {
      Integer id;
 
      Key(Integer id) {
         this.id = id;
      }
 
      @Override
      public int hashCode() {
         return id.hashCode();
      }
   }
 
   public static void main(String[] args) {
      Map<Key, String> m = new HashMap<Key, String>();
      while (true)
         for (int i = 0; i < 10000; i++)
            if (!m.containsKey(new Key(i)))
               m.put(new Key(i), "Number:" + i);
   }
}

When you execute the code above you would expect it to run forever without any problems – after all, the naive caching solution built should only expand to 10,000 elements and then the growth would stop, as all the keys are already present in the HashMap. However, this is not the case – the elements keep being added as the Key class does not contain a proper equals() implementation next to its hashCode().

The solution would be easy – add the implementation for equals() method similar to the following sample and you’re good to go. But before you manage to find the cause, you have definitely spent lost some precious brain cells.

@Override
public boolean equals(Object o) {
   boolean response = false;
   if (o instanceof Key) {
      response = (((Key)o).id).equals(this.id);
   }
   return response;
}

Second problem to keep your friend awake – String handling in some operations. Works out like a charm, especially when combined with JVM version differences. The way String internals work were changed in JDK 7u6, so if you manage to find environments where production and staging differ only by minor versions, then you are all set. Throw in the code similar to the following for your friend to debug and wonder why the problem does not surface anywhere else but in production.

class Stringer {
   static final int MB = 1024*512;
 
   static String createLongString(int length){
      StringBuilder sb = new StringBuilder(length);
      for(int i=0; i < length; i++)
         sb.append('a');
      sb.append(System.nanoTime());
      return sb.toString();
   }
 
   public static void main(String[] args){
      List substrings = new ArrayList();
      for(int i=0; i< 100; i++){
         String longStr = createLongString(MB);
         String subStr = longStr.substring(1,10);
         substrings.add(subStr);
      }
   }
}

What is happening in the code above – when it is being ran on a pre JDK 7u6, the returned substring keeps a reference to the ~1MB large String underneath. So when the sample is ran with -Xmx100m you would experience an unexpected OutOfMemoryException. Combine this with platform differences and have a different JDK version in the environment you are experimenting and the first grey hairs are about to flourish.

Did you know that 20% of Java applications have memory leaks? Don’t kill your application – instead find and fix leaks with Plumbr in minutes.

Now if you wish to cover up your tracks, we have some more advanced concepts to add into the portfolio, such as

  • Load the broken code in a different classloader and keep a reference to the class loaded after the original classloader has been discarded mimicking a classloader leak.

  • Hide the offending code into the finalize() methods making the symptoms truly unpredictable

  • Toss in a tricky combination of long-running Threads storing something in ThreadLocals being accessed by ThreadPool – governed application Threads

I hope we gave you some food for thought and some tricks to pull next time when you are mad at someone. Endless hours of hardcore debugging guaranteed. Unless your friend is using Plumbr of course which finds the leaks for him.

But blatant marketing asides, I hope we were able to demonstrate in two simple cases how easy it is to create a memory leak in Java. And most of you have experienced how hard it would be to trace down a bug like this. So if you enjoyed the post, subscribe to our Twitter feed be alerted about our future content about JVM performance tuning.

ADD COMMENT

Comments

Could you please provide us the first example without memory leak?
Thanks

Sea

Of course, it is right in this same article. You just have to implement the correct `equals` method for the `Key` class.

Nikita

In the first example: If you add instances of Key to the map and check for instances of int/Integer with containsKey a properly implemented equals method will prevent the memory leak but will not not help you with adding the entry again and again. You probably meant “if (!m.containsKey(new Key(i)))”. Proper use of generics would have prevented this issue.

Stefan Reuter

Thank you for the correct remark, fixed the sample. I guess it serves as a good case how two errors added up can give you a (partially) correct result …

Ivo Mägi

Is it possible to stop the leaks by using better quality RAM chips in my computer? I don’t really understand how memory manufacturers can get away with selling DDR that has this problem.

Is there is a good brand of RAM which doesn’t leak?

Thank you.

Weng Fu

A memory leak is not the fault of the RAM, it occurs when a computer program incorrectly manages memory allocation. In object-oriented programing, a memory leak may happen when an object is stored in memory but cannot be accessed by the running code. Meaning, memory is being used, but the program cannot access or use or stop it from being used. This usually leads to an Out of Memory Exception in extreme cases which means there is no free RAM memory for the program to start using (because it is all in use) and it causes the program to die.

z_Zelman

This is not totally correct. You’ve described ‘dangling object’, in which case object is in memory but is not accessible. It may happen in C++ applications, where programmer must manually allocate and free the memory. Such situation cannot happen in Java, where Garbage collector removes such unused objects. So, in Java memory leak is when an object is somehow referenced, thus garbage collector cannot remove it, but it is actually is not needed for the application anymore. So finding this last forgotten reference and removing it will fix the leak in Java apps.

Vladimir Šor

yes, there are several manufacturers that make unleakable RAM chips. Most notable is Kingston with its HiperX-Beast Unleakable Edition[1] and eXtreme Memory Profile compliant. notice the black silicone-sealed heatspreader that prevents any leak even at speeds as high as 2400MHz, with a 2-years Full Warranty

P.S. I’m just joking, as I hope you are as well… Sorry but I could not help to write this. Anyway, your question has already been answered.

Alied Perez