WeakReferences

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • JosAH
    Recognized Expert MVP
    • Mar 2007
    • 11453

    WeakReferences

    Greetings,

    introduction

    Java is praised (and despised) for its Garbage Collection mechanism. Over the
    years and several Java versions the GC mechanism has changed from a simple mark
    and sweep collector that caused the early Java programs to freeze temporarily
    at moments it shouldn't freeze, to a highly sophisticated multi threaded,
    complex 'generation scavenging' collection mechanism that does its work hardly
    noticable by the rest of the application or the user.

    Memory management in Java is a breeze compared to quite a lot of other programming
    languages. Still things can go wrong or run out of hand: memory leaks. To be exact,
    it's not really a 'leak' as in e.g. the C program:

    [code=c]
    int main() {
    char* p= malloc(42);
    p= malloc(54);
    }
    [/code]

    The first 42 bytes are lost for the posterity because nothing in the program
    'points' or 'refers' to it anymore. Not so in Java: a memory leak in Java
    means that something still points at a particular piece of memory that holds
    an object value, but we don't know which other thing points to it. But we're
    sure that something points to it otherwise the Garbage Collector would have
    found it.

    A Java memory leak is more like 'sticky memory' like chewing gum somewhere under
    your shoe: you don't know that it's there but it still is.

    If you have accidentally implemented such a misbehaviour in some sort of loop
    you can count on it that in the near future an OutOfMemory exception will be
    thrown by the Java Virtual Machine and your application will die a miserable death.

    A few of the core classes offer the building blocks to do something about the
    situation described above. The next paragraphs describe those building blocks.

    Weak references

    Functionally a WeakReference class is no more than this:

    [code=java]
    public class WeakReference<T > {
    private T referent;
    public WeakReference(T referent) { this.referent= referent; }
    public T get() { return referent; }
    }
    [/code]

    Note that this is not the real source code of the WeakReference class, it just
    shows how objects of this class behave functionally.

    There isn't much functionality really: you can pass a WeakReference any object
    of type T at construction time and you can get that object T back later when
    you want it again; big deal.

    Behind the scene a lot more is going on: when you instantiate a WeakReference
    object it registers itself at the Garbage Collector, i.e. it communicates with
    the enemy behind our back.

    When the Garbage Colector is activated it keeps track of the objects refered to
    by all the WeakReferences. If those objects have no other references except for
    the references held by the WeakReferences, they're pray in the claws of the
    Garbage Collector, i.e. the reference T in the WeakReference object is set to
    null and the object is lost for the posterity.

    If the T object is refered to by another object not eligible for Garbage Collection
    the T object itself will be safe though.

    Summarizing: if the only reference to an object is through a WeakReference object,
    the object is still eligible for Garbage Collection.

    If nothing else refers to a WeakReference object the referent object will only
    be safe if something else refers to that referent object directly. The WeakReference
    object however is eligible for Garbage Collection. In laymen's terms: WeakReferences
    are party poopers: when they are the last reference to an object they cowardly
    hand that object over to the Garbage Collector reaper.

    Sticky objects

    A common scenario for the sticky objects failure happens when Swing objects are
    registered as listeners to other objects. When the registered objects are not
    needed anymore they are still refered to as listeners by the other objects to
    which they were registered.

    The nasty thing is that those registered listeners can't know when they're not
    needed anymore by the application. It would be tedious for the application to
    'manually' remove those listeners when they're not needed anymore, and above,
    it would be error prone to do so (programmers are humans and humans are silly
    because they make mistakes ever so often).

    WeakReferences can be of help here: imagine we do this: instead of registering
    a listener to an object we register a WeakReference instead and make the original
    listener the referent of the WeakReference object itself. The core of the trick
    is that the 'get()' method of the WeakReference object returns null when the
    referent object has been Garbage Collected.

    For the sake of the example, let's handle ActionListeners that need to be registered
    to AbstractButtons .

    The AbstractButton contains the implementations for the following methods:

    [code=java]
    public void addActionListen er(ActionListen er listener);
    public void removeActionLis tener(ActionLis tener listener);
    [/code]

    Just because an AbstractButton happens to be the base class for everything in
    Swing that uses ActionListeners we only have to implement our new class for
    the AbstractButton class (three cheers for Swing's design!).

    If we want to register our WeakReference as an ActionListener it *has* to be
    an ActionListener itself. No problem, here goes:

    [code=java]
    public class ActionReference extends WeakReference<A ctionListener>
    implements ActionListener {
    private AbstractButton button;

    ActionReference (ActionListener listener, AbstractButton button) {
    super(listener) ;
    this.button= button;
    button.addActio nListener(this) ;
    }
    public void actionPerformed (ActionEvent event) {
    ActionListener listener= get();
    if (listener == null)
    button.removeAc tionListener(th is);
    else
    listener.action Performed(event );
    }
    }
    [/code]

    The constructor takes an AbstractButton and an ActionListener as its parameters.
    It keeps track of the button and registers itself as the ActionListener.

    When the button fires an event, the ActionReference 's actionPerformed method is
    invoked. It checks whether or not the real ActionListener has been Garbage
    Collected. If so it deregisters itself from the button. If the real listener still exists,
    the event is delegated to the real listener just as if it were registered to the
    button directly.

    For any 'button' and any 'listener' the listener is registered as follows:

    [code=java]
    new ActionReference (listener, button);
    [/code]

    That's all you have to do: when the listener is not needed anymore, our ActionReference
    takes care of it by removing itself from the source of the event. Note that I didn't use
    the 'getSource()' method from the event object itself. This protects from removing
    an ActionListener from the wrong object if the firing of the event was synthesized
    by the application itself.

    This single little class prevents memory leaks, or 'sticky objects' from the application.
    I'm sure you can figure out how to safely handle other types of event firing yourself now.

    Hopefully 'till next week and

    kind regards,

    Jos
Working...