[Biojava-dev] memory leak

mark.schreiber at group.novartis.com mark.schreiber at group.novartis.com
Fri Jan 28 04:12:05 EST 2005


Looking at this further.

The parent SymbolList adds ChangeListeners to all it's children that are 
created during subList() or subStr() operations. The children of these 
subXXX operations are views on the parent. Any Edit operations performed 
on them are notified to the parent and applied to the parent (who knew? 
how cool!). So that answers the question about what they are for and also 
why they are not cleaned up.

Following your clue about the reapGarbageListeners() method your correct 
that it is never called. The only place it can be called is from 
ChangeSupport.firePreChangeEvent or ChangeSupport.firePostChangeEvent. 
Because nothing we are doing in the loop is actually causing anything to 
change there is no chance for reapListeners() to be called.

To test this out I changed the loop to:

    while(true){
      SymbolList sl2 = sl.subList(1,12);
      Edit e = new Edit(4, DNATools.getDNA(), DNATools.g());
      sl2.edit(e);
    }

This forces a changeEvent and results in garbageCollection of the 
WeakReferences and bingo, no more memory leak. 

So, there needs to be some mechanism by which reapGarbageListeners() can 
be called without the need for a changeEvent. I'm thinking that the 
finalize() method of SimpleSymbolList might call it but this is not 
currently possible as the reapListeners() method is protected and in a 
different package. Maybe the finalize() method could throw a dummy 
changeEvent as a bit of a hack and to signal to the world it is dying.

Matthew or Thomas, Any ideas??

- Mark






Rainer Pudimat <rpudimat at informatik.uni-jena.de>
01/27/2005 11:19 PM

 
        To:     biojava-dev at biojava.org, Mark Schreiber/GP/Novartis at PH
        cc: 
        Subject:        Re: [Biojava-dev] memory leak


Hello Mark,

I have tried your suggested change in ChangeSuppot.removeChangeListener, 
but without positive effect on my heap.

The problem is that the condition
if( (listeners[i].get() == cl) && (types[i] == ct) )
is never fullfilled so your change is never executed.

But anyway, it seems the right place to look for.
Inspired by the method next to removeChangeListener
(reapGarbageListener) I tried to test what Objects
to which References listeners[i] points to, are actually
null:

if( ((listeners[i].get() == cl) && (types[i] == ct)) || 
listeners[i].get() == null )

This is true in many cases.

On the other hand, the method reapGarbageListener was never
executed, independent of the number of
System.gc() calls.

Rainer.

mark.schreiber at group.novartis.com schrieb:
> The problem seems to be in ChangeSupport.removeChangeListener()
> 
>   /**
>    * Remove a listener that was interested in a specific types of 
changes.
>    *
>    * @param cl  a ChangeListener to remove
>    * @param ct  the ChangeType that it was interested in
>    */
>   public void removeChangeListener(ChangeListener cl, ChangeType ct) {
>     synchronized(this) {
>       for(int i = 0; i < listenerCount; i++) {
>         if( (listeners[i].get() == cl) && (types[i] == ct) ) {
>           listenerCount--;
>           System.arraycopy(listeners, i+1, listeners, i, (listenerCount 
- 
> i));
>           System.arraycopy(types, i+1, types, i, (listenerCount - i));
>           return;
>         }
>       }
>     }
> 
> I think that somewhere there needs to be a nullification of the value at 

> listeners[i]. This is a classic memory leak problem outlined in Item 5 
of 
> Joshua Bloch's Effective Java. I think that the loop should be:
> 
> 
>   public void removeChangeListener(ChangeListener cl, ChangeType ct) {
>     synchronized(this) {
>       for(int i = 0; i < listenerCount; i++) {
>         if( (listeners[i].get() == cl) && (types[i] == ct) ) {
> 
>           //prevent memory leak
>           listeners[i] = null;
> 
>           listenerCount--;
>           System.arraycopy(listeners, i+1, listeners, i, (listenerCount 
- 
> i));
>           System.arraycopy(types, i+1, types, i, (listenerCount - i));
>           return;
>         }
>       }
>     }
> 
> Does this look correct to everyone? I'm still a little concerned about 
> that HashMap though as I don't see it here. It also doesn't address the 
> question of what are all these ChangeListeners needed for (but then I'm 
no 
> expert on the Changeable API).
> 
> - Mark
> 
> 
> 
> 
> 
> Mark Schreiber/GP/Novartis at PH
> Sent by: biojava-dev-bounces at portal.open-bio.org
> 01/27/2005 01:25 PM
> 
> 
>         To:     Michael Heuer <heuermh at acm.org>
>         cc:     rpudimat at informatik.uni-jena.de, 
biojava-dev at biojava.org, Michael Heuer 
> <heuermh at shell3.shore.net>, (bcc: Mark Schreiber/GP/Novartis)
>         Subject:        Re: [Biojava-dev] memory leak
> 
> 
> 
>>   synchronized(this) {
>>     growIfNecessary();
>>     types[listenerCount] = ct;
>>     listeners[listenerCount] = new WeakReference(cl);
>>     listenerCount++;
>>   }
> 
> 
> 
> This may be the source of the problem. The thing the WeakReference 
points 
> to is released when memory runs low but a reference to the WeakReference 

> is being maintained (possibly in the listeners[]).
> 
> - Mark
> _______________________________________________
> biojava-dev mailing list
> biojava-dev at biojava.org
> http://biojava.org/mailman/listinfo/biojava-dev
> 
> 
> 





More information about the biojava-dev mailing list