nicolas brodu
Tricky Java...
When an object registered as listener becomes unused, the fact that the listener list holds
a reference to it prevents it from beeing fred by the garbage collector. This ends up in
objects not being fred, and growing listener lists of unused objects.
This is bad, and listener lists are a very common source of memory leak! Beware!
A naive and developper-unfriendly approach would be to tell all registering
objects to unregister before they are unsued... This is easy to do in C++ where
there are proper destructors, but in Java the finalize() method is precisely called
by the garbage collector, which won't call it in the present case since the listener
list still holds a reference. Back to the beginning. An even-more developer unfriendly
approach would impose a constraint on all objects using objects registered as listeners
to do the cleanup, but this is a real mess and does not even work for other reasons (for
example, think of inner classes registering private members as listeners, or other awful
cases where the object using another object using another object using... has no way of
unregistering a listerner without modifying drastically the API just for that).
Weak references were introduced in Java 1.2 to solve this problem. If only weak references
to an object remains, the garbage collector discards it. The get() method of the weak reference
holder then returns null. A queue can be used to be notified when this happens, as the
application does not decides when the object is finalized (this is handled by the garbage
collector thread).
Thus, weak reference pointers may become invalid any time!
For JSynoptic, we have an additional problem. The same object may be registered as listener
several times, but should be notified only once. See EndNotificationListener for a concrete
example of this. Also, a Set is not adapted to hold only one reference of each listener.
If an object is registered twice by two different paths and unregistered only once, it
is still expected to be notified by the second registering path. Hence, it must still
be present in the listener list, which would not be the case using a Set.
The solution is of course reference counting, which is applied on the register/unregister
operation. This is complementary to the WeakReference mecanism : if an object forgets to
unregister or doesn't have the occasion to do it before being thrown away, then the weak
reference mecanism will still discard it even if the refcount field is >1, which is what we
want. On the other hand, if the refcount field goes down to 0, the object is removed from the
listener list, but this does not necessarily mean the listener is unused. It may just have
unregistered and live its life as usual. As in this case the listener list does not hold any
reference to the object, it cannot be the source for a memory leak.
So, the refcount field is NOT related to the weak reference mecanism. It is necessary for
another reason entirely, as decribed above.
See the notes below for more fun with the ReferenceCountedPointer class.
It holds a weak reference to the ListenerManager, so as to avoid the same problem
as above, but for the managers themselves.