In some cases you will wish to have the cache of objects, but what if these objects are registered listeners of some other objects? This hard link between the cached items and persistent ones will not let the first to be garbage collected. The article shows how this misbehavior can be easily avoided using soft-referenced wrapper for your listeners.

About a week ago we noticed that low-memory PC is significantly slowing down when running BlogBridge for several hours. It became clear almost from the start of investigation that the problem is exactly in the memory limitation or, to be correct, in application itself, quickly running out of limits. Soon I began to suspect that one of the caches isn’t getting cleared during garbage collection as appropriate and it was the case. Well, I need to let you know about some internals – what’s that cache and what are the elements of it. In BlogBridge we render each article of the feed on a separate panel. Each panel is an JEditorPane component plus title, plus date label and etc. Everything isn’t very greedy in terms of memory consumption, but it takes a while to create each thing. The cache, as you might already guessed, is intended to keep previously rendered panels for a while in case if they will be necessary again soon. So, at some moment in development the cache stopped to be cleared during GC and it led to the OOM’s.

To be honest, at first I thought that the problem is Mac-specific as it was found on Mac first and the cache stress test showed the problem there, but after the review of unit tests and some adjustments I found that it’s not specific to Mac, but pretty general. Looking into the code of the panel component, I found that the reason for that is in the shared reference to itself, given to external objects when registering as a property change listener. It was clear that it needs that listening and it’s not acceptable to stop doing registration. The solution came from the different direction and I would like to share it with you in this writing.

The problem, as it was said before, in a property change listener registration. The situation (very simplified) looks as follows:

class ArticlePanel implements PropertyChangeListener
{
    public ArticlePanel(EventSource source)
    {
        source.addPropertyChaneListener(this);
    }
}

You can see that we put each new ArticlePanel object in the listeners list of EventSource object which will prevent ArticlePanel object from being garbage collected while EventSource object stays in memory. If EventSource object stays in memory all the way we get into really big trouble and the OOM exception is only matter of time.

The solution I came to is an indirect soft-referenced PCL wrapper. The idea was to wrap the actual PCL – the ArticlePanel in our case – with PCL implementation based on SoftReference (WeakReference is also possible, but it is less memory-sensitive) class to remove the direct link between emitter (EventSource) and consumer (ArticlePanel) to allow the listener to be garbage collected when no other references exist. Here is how the wrapper thing looked at first:

public class SoftPCLWrapper extends SoftReference
    implements PropertyChangeListener
{
    /**
     * Creates wrapper for the listener.
     *
     * @param listener listener.
     */
    public SoftPCLWrapper(PropertyChangeListener listener)
    {
        super(listener);
    }

    /**
     * This method gets called when a bound property is changed.
     *
     * @param evt event object.
     */
    public void propertyChange(PropertyChangeEvent evt)
    {
        PropertyChangeListener listener = (PropertyChangeListener)get();

        if (listener != null)
        {
            listener.propertyChange(evt);
        }
    }
}

Nice, isn’t it? Now let’s modify our ArticlePanel to benefit from this new invention.

class ArticlePanel implements PropertyChangeListener
{
    public ArticlePanel(EventSource source)
    {
        source.addPropertyChaneListener(new SoftPCLWrapper(this));
    }
}

The only thing I am worrying about is even if the actual listener (ArticlePanel) will be garbage collected the emitter (EventSource) will still maintain the reference to soft wrapper in its listeners list and will continue to send notifications to it. It isn’t good because we can flood the lists of listeners with our wrappers and get the OOM again. Sure it’s a longer way to go, but it is a definite memory leak. Here is a small enhancement to SoftPCLWrapper.

public abstract class SoftPCLWrapper extends SoftReference
    implements PropertyChangeListener
{
    // ... skipped ...

    /**
     * This method gets called when a bound property is changed.
     *
     * @param evt event object.
     */
    public void propertyChange(PropertyChangeEvent evt)
    {
        PropertyChangeListener listener = (PropertyChangeListener)get();

        if (listener == null)
        {
            removeThisListener(evt.getSource());
        } else
        {
            listener.propertyChange(evt);
        }
    }

    /**
     * This method should be overridden with exact self-unregistering logic.
     * It will be called when it is detected that the wrapped listener is no longer
     * existing and this wrapper can be safely removed from the source of events.
     *
     * @param source source of events.
     */
    protected abstract void removeThisListener(Object source);
}

Note that the class became abstract and new method was added – removeThisListener(). When property change event comes and wrapper sees that it has no actual listener any longer (it was garbage collected), the wrapper calls removeThisListener() method, passing the source of event as parameter. Any concrete implementation of this wrapper class should provide means for removing this soft wrapper instance from the list of listeners in the events source.

Let me modify ArticlePanel code again to follow these new rules:

class ArticlePanel implements PropertyChangeListener
{
    public ArticlePanel(EventSource source)
    {
        source.addPropertyChaneListener(new SoftPCLWrapper(this)
        {
            protected void removeThisListener(Object source)
            {
                EventSource src = (EventSource)source;
                src.removePropertyChangeListener(this);
            }
        });
    }
}

Another way is to move the particular soft wrapper class definition into the EventSource class itself, like this:

class EventSource
{
    public void addPropertyChangeListener(PropertyChangeListener l) { ... };
    public void removePropertyChangeListener(PropertyChangeListener l) { ... };

    public static class PCLWrapper extends SoftPCLWrapper
    {
            protected void removeThisListener(Object source)
            {
                EventSource src = (EventSource)source;
                src.removePropertyChangeListener(this);
            }
    }
}

class ArticlePanel implements PropertyChangeListener
{
    public ArticlePanel(EventSource source)
    {
        source.addPropertyChaneListener(new EventSource.PCLWrapper(this));
    }
}

This way, you bring minimum changes to original consumer, while having particular soft wrapper and target emitter definitions near to each other. Now that the listeners linked to emitters indirectly through soft references nothing prevents them to be garbage collected. Hope that it was interesting and useful to you!