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:
Expand|Select|Wrap|Line Numbers
- int main() {
- char* p= malloc(42);
- p= malloc(54);
- }
'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:
Expand|Select|Wrap|Line Numbers
- public class WeakReference<T> {
- private T referent;
- public WeakReference(T referent) { this.referent= referent; }
- public T get() { return referent; }
- }
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:
Expand|Select|Wrap|Line Numbers
- public void addActionListener(ActionListener listener);
- public void removeActionListener(ActionListener listener);
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:
Expand|Select|Wrap|Line Numbers
- public class ActionReference extends WeakReference<ActionListener>
- implements ActionListener {
- private AbstractButton button;
- ActionReference(ActionListener listener, AbstractButton button) {
- super(listener);
- this.button= button;
- button.addActionListener(this);
- }
- public void actionPerformed(ActionEvent event) {
- ActionListener listener= get();
- if (listener == null)
- button.removeActionListener(this);
- else
- listener.actionPerformed(event);
- }
- }
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:
Expand|Select|Wrap|Line Numbers
- new ActionReference(listener, button);
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