Mobile Zone is brought to you in partnership with:

Rafael is a software engineer at Kantega based in Oslo. He is a Java enthusiast with particular interests in byte code engineering, functional programming, multi-threaded applications and the Scala language. Rafael is a DZone MVB and is not an employee of DZone and has posted 5 posts at DZone. You can read more from them at their website. View Full User Profile

Memory Leaks and Memory Management in Java Applications

01.02.2014
| 19030 views |
  • submit to reddit

One of the more prominent features of the Java platform is its automatic memory management. Many people translate this feature erroneously into there are no memory leaks in Java. However, this is not the case and I am under the impression that modern Java frameworks and Java-based platforms, especially the Android platform, increasingly contradict this erroneous assumption. In order to get an impression on how memory leaks can occur on the Java platform, look at the following implementation of a stack:

class SimpleStack {
 
    private final Object[] objectPool = new Object[10];
    private int pointer = -1;
 
    public Object pop() {
        if(pointer < 0) {
            throw new IllegalStateException("no elements on stack");
        }
        return objectPool[pointer--];
    }
 
    public Object peek() {
        if(pointer < 0) {
            throw new IllegalStateException("no elements on stack");
        }
        return objectPool[pointer];
 
    }
 
    public void push(Object object) {
        if(pointer > 8) {
            throw new IllegalStateException("stack overflow");
        }
        objectPool[++pointer] = object;
    }
}

This stack implementation stores its content in form of an array and additionally manages an integer which points to the currently active stack cell. This implementation introduces a memory leak every time an element is popped off the top of the stack. More precisely, the stack keeps a reference to the top element in the array, even though it will not be used again. (Unless it is pushed onto the stack again what will cause the reference to be overridden with the exact same reference.) As a consequence, Java will not be able to garbage collect this object even after all other references to the object are released. Since the stack implementation does not allow direct access to the underlying object pool, this unreachable reference will prevent garbage collection of the referenced object, until a new element is pushed onto the same index of the stack.

Fortunately, this memory leak is easy to fix:

public Object pop() {
        if(pointer < 1) {
            throw new IllegalStateException("no elements on stack");
        }
        try {
            return objectPool[pointer];
        } finally {
            objectPool[pointer--] = null;
        }
    }

Of course, the implementation of a memory structure is not a very common task in day to day Java development. Therefore, let us look at a more common example of a Java memory leak. Such a leak is often introduced by the commonly used observer pattern:

class Observed {
 
    public interface Observer {
        void update();
    }
 
    private Collection<Observer> observers = new HashSet<Observer>();
 
    void addListener(Observer observer) {
        observers.add(observer);
    }
 
    void removeListener(Observer observer) {
        observers.remove(observer);
    }
 
}

This time, there exists a method that allows to directly remove a reference from the underlying object pool. As long as any registered observer gets unregistered after its use from the outside, there are no memory leaks to fear in this implementation. However, imagine a scenario where you or the user of your framework forget to deregister the observer after its use. Again, the observer will never be garbage collected because the observed keeps a reference to it. Even worse, without owning a reference to this now useless observer, it is impossible to remove the observer form the observed's object pool from the outside.

But also this potential memory leak has an easy fix which involves using weak references, a Java platform feature that I personally wished programmers would be more aware of. In a nutshell, weak references behave like normal references but do not prevent garbage collection. Thus, a weak reference can suddenly be found being null if there were no strong references remaining and the JVM performed a garbage collection. Using weak references, we can change the above code like this:

private Collection<Observer> observers = Collections.newSetFromMap(
        new WeakHashMap<Observer, Boolean>());

The WeakHashMap is a ready-made implementation of a map that wraps its keys with weak references. With this change, the observed will not prevent garbage collection of its observers. However, you should always indicate this behavior in your Java docs! It can be quite confusing, if users of your code want to register a permanent observer to your observant like a logging utility to which they do not plan to keep a reference to. For example, Android's OnSharedPreferencesChangeListener uses weak references to is listeners without documenting this feature. This can keep you up at night!

In the beginning of this blog entry, I suggested that many of today's frameworks require careful memory management by their users and I want to give at least two examples on this topic to explain this concern.

Android platform:

Programming for Android introduces a life cycle programming model to your core application classes. All in all, this means that you are not in control of creating and managing your own object instances of these classes but that they will instead by created by the Android OS for you whenever they are needed. (As for example if your application is supposed to show a certain screen.) In the same manner, Android will decide when it does not longer need a certain instance (as when your application's screen was closed by the user) and inform you about this removal by calling a specific life cycle method on the instance. If you however let a reference to this object slip away into some global context, the Android JVM will not be able to garbage collect this instance contrary to its intent. Since Android phones are usually rather restrained in memory and because Android's object creation and destruction routines can grow pretty wild even for simple apps, you have to take extra care to clean up your references.

Unfortunately, a reference to a core application class slips away quite easily. Can you spot the slipped reference in the following example?

class ExampleActivity extends Activity {
 
    @Override
    public void onCreate(Bundle bundle) {
        startService(new Intent(this, ExampleService.class).putExtra("mykey",
                new Serializable() {
                    public String getInfo() {
                        return "myinfo";
                    }
                }));
    }
}
If you thought, it the this reference in the intent's constructor, you are wrong. The intent only serves as a starting command to the service and will be removed after the service has started. Instead, the anonymous inner class will hold a reference to its enclosing class which is the ExampleActivity class. If the receiving ExampleService keeps a reference to the instance of this anonymous class, it will as a consequence also keep a reference to the ExampleActivity instance. Because of this, I can only suggest to Android developers to avoid the use of anonymous classes.

Web application frameworks (Wicket, in particular):

Web application frameworks usually store semi-permanent user data in sessions. Whatever you write into a session will usually stay in memory for an undetermined period of time. If you litter up your sessions while having a significant number of visitors, your servlet container's JVM will pack up sooner or later. An extreme example of needing to take extra care of your references is the Wicket framework: Wicket serializes any page a user visited in a versioned state. Oversimplified, this means that if one of your website's visitors clicks your welcome page ten times, Wicket will in its default configuration store ten serialized objects on your hard drive. This requires extra care because any references hold by a Wicket page object, will cause the references objects to be serialized together with the page. Look for example at this bad practice Wicket example:

class ExampleWelcomePage extends WebPage {
 
    private final List<People> peopleList;
 
    public ExampleWelcomePage (PageParameters pageParameters) {
        peopleList = new Service().getWorldPhonebook();
    }
}
By clicking your welcome page ten times, your user just stored ten copies of the world's phone book on your servers hard drive. Therefore, always use LoadableDetachableModels in your Wicket applications which will take care of the reference management for you.

Tracing memory leaks in Java applications can be tiresome and therefore, I want to name JProfiler as a useful (but unfortunately non-free) debugging tool. It allows you to browse through the insides of your Java running application in form of for example heap dumps. If memory leaks are a problem for your applications, I recommend to give JProfiler a shot. There is an evaluation license available.

For further reading: If you want to see another interesting occurrence of memory leaks when you are customizing class loaders, refer to the Zeroturnaround blog.


Published at DZone with permission of Rafael Winterhalter, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Lee Hong replied on Tue, 2014/01/07 - 9:36pm

under java 6, substring will result in memory leak too

Rafael Winterhalter replied on Wed, 2014/01/08 - 10:36am in response to: Lee Hong

 This is true but by far not the only example throughout the JDK classes that offers potential for memory leaks. In particular dangerous are classes that make use of a thread's context class loader.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.