A Weak Listener

Now that you have a tested collection that doesn't hold objects in memory, you can use it to break the loop of strong references. Instead of holding the listeners in a normal set, put them in WeakHashSet. To do this, you will have to rewrite the property change support class and invent your own support class that does the same job with weak listeners. The code for PropertyChangeSupport is in the oracle.hcj.references package. This PropertyChangeSupport class implements the identical interface as the stock java.beans.PropertyChangeSupport class except that it uses weak references to store the listeners. It allows the same firing of events that the original JDK class does without pinning the listeners in memory. If one of the listeners decides to go away, the property change support class will simply note this fact and move on. The class is implemented using a regular HashMap to store the listeners. The keys in the HashMap (listenerMap) are the named properties that the listener is interested in. The values in the HashMap are instances of WeakHashSet. This allows the listener to register to receive events on single properties. For listeners that want to receive events on all properties of the class, an ALL_PROPERTIES key is used to store the set. To avoid conflicting keys in your map, the value of the ALL_PROPERTIES key is a string that would be illegal to use as a property name:

package oracle.hcj.references;
public class PropertyChangeSupport {
 private static final String ALL_PROPERTIES = "**GENERAL**";


The declaration of the constructor initializes the HashMap with the ALL_PROPERTIES key and the keys for the other properties in the class:

 public PropertyChangeSupport(final Object producer) {
 try {
 final BeanInfo info = Introspector.getBeanInfo(producer.getClass( ));
 final PropertyDescriptor[] props = info.getPropertyDescriptors( );
 for (int idx = 0; idx < props.length; idx++) {
 listenerMap.put(props[idx].getName( ), new WeakHashSet( ));
 }
 listenerMap.put(ALL_PROPERTIES, new WeakHashSet( ));
 this.producer = producer;
 } catch (IntrospectionException ex) {
 throw new RuntimeException(ex);
 }
 }


Reflection and the weak hash set are used in your constructor. To support named property listeners, create a HashMap of WeakHashSet objects. In addition to having one for each property of the object, add an extra object with the DUMMY key to the listeners of all properties. The end result is that the mirror.event.PropertyChangeSupport class is interchangeable with Java's java.beans.PropertyChangeSupport class. Let's interchange them in MutableObject. To do this, simply change one line:

import java.beans.PropertyChangeSupport;
public abstract class MutableObject implements Serializable {
 // . . . same as before!!
}


becomes:

import oracle.hcj.references.PropertyChangeSupport;
public abstract class MutableObject implements Serializable {
 // . . . same as before!!
}


Using this class, break the loop of strong references. The GUI panel will now hold onto your data objects with strong references. However, the data objects will hold onto the GUI panels with weak references. Screenshot-8 shows how the resulting structure looks in a UML diagram.

Screenshot-8. Using the PropertyChangeSupport object
Java figs/HCJ_1108.gif

Since the PropertyChangeSupport object holds onto the GUI panel with a weak reference, there is no loop of strong references such as those in Screenshot-3. If the panel removes itself from the PropertyChangeSupport, that is fine with the data object. If it forgets to remove itself, that is also fine because the weak reference won't hold the panel in memory, and the panel will be removed passively. The potential for memory leaks is zero. Now, no matter what happens, when the GUI panel goes out of scope, everything is released.

      
Comments