Not garbage collected DefaultMultipleCDockable instance

hi all,

I set the “remove on close” flag on a DefaultMultipleCDockable instance to true by calling setRemoveOnClose(true). I’ve expected that if it is closed, then this instance will be completely removed and at the end it will be garbage collected. After i’ve got OutOfMemory error, i noticed that the DefaultMultipleCDockable instances were not garbage collected.DefaultMultipleCDockable is referenced by DefaultCommonDockable, which again is referenced by some listener (LocatedListenerList??) etc.

Is this a bug? If not, how to remove the instance completely?

best wishes,
ilhami visne

P.S.: I’ve used the Java VisualVM to look at the references.

“remove on close” should remove the Dockable completely, so it is some kind of bug. The connection between DefaultCommonDockable and DefaultMultipleCDockable is intended, but the listeners should be removed.

This morning I’ve tried to create the leak myself but failed, the Dockables always got cleaned up. This does not mean, that there is no leak. Any chance you could provide me your test case/application?

thanks for quick reply.

I’ve slightly changed the one of the test program provided in the distribution, DockingFramesTester. The code is below. And i created some screenshots from the Java VisualVM, which may helps.

The following two pictures are before closing:

http://i35.tinypic.com/2h3dki9.jpg

This pic shows two parts, one the instance itself and its fields and the other the references to the instance

http://i37.tinypic.com/2dch4ap.jpg

And these two after it is closed.

instance count is still 1 (see the marked area)

http://i33.tinypic.com/6zsv2v.jpg

And the existing references to the instance.

http://i34.tinypic.com/144bibo.jpg

import java.awt.GridLayout;

import javax.swing.JFrame;
import javax.swing.UIManager;

import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CLocation;
import bibliothek.gui.dock.common.CWorkingArea;
import bibliothek.gui.dock.common.DefaultMultipleCDockable;
import bibliothek.gui.dock.common.EmptyMultipleCDockableFactory;
import bibliothek.gui.dock.common.MultipleCDockable;
import bibliothek.gui.dock.common.MultipleCDockableFactory;
import bibliothek.gui.dock.common.MultipleCDockableLayout;
import bibliothek.gui.dock.common.action.CButton;
import bibliothek.gui.dock.common.layout.ThemeMap;

public class DockingFramesTester2 extends JFrame {
	
	private MultipleCDockableFactory<MultipleCDockable, MultipleCDockableLayout> multiDockFactory = 
		new EmptyMultipleCDockableFactory<MultipleCDockable>() {
			@Override
			public MultipleCDockable createDockable() {
				return new DefaultMultipleCDockable(this);
			}
		};
		
	private void setup(CControl control) {
		control.addMultipleDockableFactory("Files", multiDockFactory); 
		
		//center area for Files
		CWorkingArea workArea = control.createWorkingArea("editorArea") ;
		workArea.setLocation(CLocation.base().normal()) ;
		workArea.setVisible(true) ;
		
		//add new files to the work area
		final NewFile newFileAction = new NewFile(workArea);
		newFileAction.action() ;
	}
	
	public class NewFile extends CButton {
		private int ctr = 0;
		private CWorkingArea workArea ;
		
		public NewFile(CWorkingArea workArea) {
			this.workArea = workArea ;
			setText("New Prcess") ;
		}
		
		@Override
		protected void action() {
			MultipleCDockable dockable = createFile(workArea, "File " + (++ctr));
			dockable.setLocation(CLocation.working(workArea)) ;
			dockable.setVisible(true) ;
		}
		
		private MultipleCDockable createFile(CWorkingArea workArea, String title) {
			DefaultMultipleCDockable dockable = new DefaultMultipleCDockable(multiDockFactory, title);
			dockable.setRemoveOnClose(true);
			dockable.setCloseable(true) ;
			dockable.setExternalizable(false) ;
			workArea.add(dockable) ;
			
			return dockable;
		}
	}
	
	public static void main(String[] args) {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch(Exception ex){
			ex.printStackTrace() ;
		}
		
		DockingFramesTester2 tester = new DockingFramesTester2();
		tester.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) ;
		
		CControl control = new CControl(tester);
		//control.setTheme(ThemeMap.KEY_SMOOTH_THEME);
		control.setTheme(ThemeMap.KEY_ECLIPSE_THEME);
		tester.add(control.getContentArea());
		
		tester.setup(control) ;
		
		tester.setLayout(new GridLayout(1,1)) ;
		tester.setSize(800, 600) ;
		tester.setLocationRelativeTo(null) ;
		
		tester.setVisible(true) ;
	}
}

Thanks, now I see the same thing. This might take a while to repair…

I found the bug and fixed it, I’ll upload a new version on the next weekend (this fix is already in the repository, if you desperately need it).

The bug happened this way: “SimpleDockAction.KeyForwarder” adds a “DockHierarchyListener” to a Dockable. If the DockController of the Dockable changes, the KeyForwarder updates some internal properties and adds or removes listeners to other modules. If the Dockable gets removed, the KeyForwarder gets destroyed (there is a method “destroy…”). The KeyForwarder also removes its listener from the Dockable if destroyed.

All of that worked very well. But this framework allows to remove a listener while there is an event firing. The listener will receive the event even if it is no longer interested in the event. And in this case, the KeyForwarder received an event and reacted by adding a listener to some module… ergo the garbage collector could not remove the KeyForwarder. And the KeyForwarder contains a reference to a Dockable.

In the test I used there was no SimpleDockAction around, hence the bug did not appear.

So quick, thanks. you are great.

cheers,
ilhami visne

Sorry, got some RL-issues in my way. The update gets a delay, but the bugfix is in the repository (user=anonymous, pw=anon)