[Biojava-l] ChangeSupport, WeakReferences, & docs

Rhett Sutphin rhett-sutphin at uiowa.edu
Thu Mar 20 14:10:29 EST 2003


Hi,

I am using org.biojava.utils.ChangeSupport in a application I'm 
writing.  I noticed some odd behavior yesterday:  some of my 
ChangeListeners would stop being notified after the program had been 
executing for a short while.  After some false starts, I looked at the 
ChangeSupport source and discovered that it only maintains weak 
references to the ChangeListeners registered in it.

After looking through the mailing list archives, I understand why this 
is desirable in some instances (see 
http://www.biojava.org/pipermail/biojava-l/2000-December/000578.html).  
Unfortunately, it conflicts with a common java-event-handling idiom:  
the inner class event handler (anonymous or not).  When you do 
something like this:

chgable.addChangeListener(new ChangeAdapter() {
     public void postChange(ChangeEvent ce) {
         doSomething();
     }
});

or this:

chgable.addChangeListener(new InnerChangeHandler());

where chgable uses ChangeSupport, it silently stops working shortly 
after the application starts up because the only reference to the event 
handler is the weak reference in the ChangeSupport object.

Since the WeakReference use is, on one hand, a reasonable solution to a 
memory-efficiency problem but, on the other hand, silently causes odd 
behavior in some instances, I propose that this should be documented.  
Something like this might be sufficient:

   ---
Warning:  ChangeSupport only maintains weak references to registered 
ChangeListeners.  This means that any ChangeListener registered to it 
will stop receiving change events (because it is garbage-collected) as 
soon as all other references to the ChangeListener go out of scope.  
Classes that use ChangeSupport are advised to document this, as it will 
make anonymous event handlers fail to behave as expected.
   ---

In addition to this, it might be reasonable to add a version of 
ChangeSupport that uses strong references for those who are aware of 
the risks and don't care.  A quick-and-dirty version of such a class 
appears below.

Rhett

------ StrongRefChangeSupport.java ------

import org.biojava.utils.ChangeSupport;
import org.biojava.utils.ChangeListener;
import org.biojava.utils.ChangeType;

import java.util.Set;
import java.util.HashSet;

/**
  * An implementation of ChangeSupport that maintains strong references
  * to registered ChangeListeners.  This makes it suitable for use with
  * anonymous ChangeListeners, but introduces the potential for memory
  * leaks when a large object listens to a long-lived object.
  */
public class StrongRefChangeSupport extends ChangeSupport {
     private Set strongRefs;

     public StrongRefChangeSupport() {
         super();
         initStrongRefs();
     }

     public StrongRefChangeSupport(int initialSize) {
         super(initialSize);
         initStrongRefs();
     }

     public StrongRefChangeSupport(int initialSize, int delta) {
         super(initialSize, delta);
         initStrongRefs();
     }

     public StrongRefChangeSupport(Set unchanging) {
         super(unchanging);
         initStrongRefs();
     }

     public StrongRefChangeSupport(Set unchanging, int initialSize, int 
delta) {
         super(unchanging, initialSize, delta);
         initStrongRefs();
     }

     private void initStrongRefs() {
         strongRefs = new HashSet();
     }

     public void addChangeListener(ChangeListener cl, ChangeType ct) {
         super.addChangeListener(cl, ct);
         strongRefs.add(new Key(cl, ct));
     }

     public void removeChangeListener(ChangeListener cl, ChangeType ct) {
         super.removeChangeListener(cl, ct);
         strongRefs.remove(new Key(cl, ct));
     }

     private static class Key {
         private final ChangeListener cl;
         private final ChangeType ct;
         public Key(ChangeListener cl, ChangeType ct) {
             this.cl = cl;
             this.ct = ct;
         }

         public boolean equals(Object other) {
             if (!(other instanceof Key)) return false;
             Key oKey = (Key) other;
             return (oKey.cl == this.cl && oKey.ct == this.ct);
         }

         public int hashCode() {
             return cl.hashCode() + ct.hashCode();
         }
     }
}

------ StrongRefChangeSupport.java ------

--
Rhett Sutphin
Research Assistant (Software)
Coordinated Laboratory for Computational Genomics
   and the Center for Macular Degeneration
University of Iowa - Iowa City, IA 52242 - USA
4111 MEBRF - email: rhett-sutphin at uiowa.edu



More information about the Biojava-l mailing list