How to create a memory leak

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(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<string> substrings = new ArrayList<string>();
      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. Now if you wish to cover up your tracks, we have some more advanced concepts to add into the portfolio, such as 3d Tap with giant drip

  • 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.
 

Reference: How to create a memory leak from our JCG partner Nikita Salnikov-Tarnovski at the Plumbr Blog blog.
Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

One Response to "How to create a memory leak"

  1. iWumbo says:

    Love this post. It’s great help to test out my memory. Also great to give to someone who I dont like lol. JK

    Anyways, if having resources such as scanners and such, which you never close, but are still running in the ‘background,’ do they cause the memory to leak? Or does the JVM simply close these resources itself?

Leave a Reply


× 6 = twenty four



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

15,153 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books