0

Maybe some of you can help me solving a very strange issue with java and JComboboxes. I tried several hours to trace down the problem but I cant find a solution. I don't want to paste a huge code here, but this simple loop demonstrates it:

    JComboBox cb;
    for(int i=0;i<1000;i++)
    {
        cb=new JComboBox();
    }

I can run this code wherever I want, the 1000 ComboBoxes are never GCed and I do not understand, why???

BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • 1
    Do you run out of memory? Otherwise the GC could just have decided not to clean up anything yet... – The Nail Jan 21 '12 at 22:53
  • no, not in this sample, but in the huge app i use it, i ran into GC, but i tried this simple code and it works with all (buttons, tables, etc...) but not with the combobox. tried with 100000 and i run out of memory :-( – David Rousal Jan 21 '12 at 23:07
  • 1
    For reported jcombobox memory leaks, google for 'site:bugs.sun.com jcombobox memory leak', but first just run the sample by looping until you run out of memory, to be sure it is a memory leak. – The Nail Jan 21 '12 at 23:14
  • my curiosity for why reason in this workd you needed to test this issue – mKorbel Jan 22 '12 at 00:13
  • i have a huge project and found a big memory issue with comboboxes and I tried to find some bug in my project till i teared it down to swing level again anf found this strange behaviour. It was not just trying this sample without any purpose :-) – David Rousal Jan 22 '12 at 00:37
  • 1
    You might try profiling your example over time, as shown [here](http://stackoverflow.com/a/6310284/230513). – trashgod Jan 22 '12 at 04:46
  • i profiled it using jvisualvm, thats why i found out that this is happening, but there are so many internal references, i cant find the way :-) – David Rousal Jan 22 '12 at 15:08
  • @David Rousal that's about I talking, if you are hockey in the referencies, then you have to disable/enable code executions step_by_step, 1) disable any recreate top_level Containers and reuse that 2) create only one Model for each of JCOmboBox, 2) if JComboBox take value from database, then close statement and resultset in the finally block, if from File then File and stream close in the finally block, 3) don't remove/add JComponents on Runtime, use CardLayout for switching betweens views – mKorbel Jan 22 '12 at 17:16
  • thx for your input mKorbel, I will give it a try – David Rousal Jan 22 '12 at 20:54

3 Answers3

3

A JComboBox creates a DefaultListModel with a listener to the combobox. So garbage collection on such an object cluster is postponed. However after the fourth run with me either the garbage was collected or the JIT found it did not need to create those objects.

Maybe your problem was that calling explicitly System.gc() not cleaning it up? That I can imagine.


*How to trace the problem

I tried the following to exclude components.

private static class ReducedJComboBox<T> extends JComboBox<T> {

    @Override
    public void setEditor(ComboBoxEditor anEditor) {
    }

}

public static void main( String[] args )
{
    System.out.println("; " + Runtime.getRuntime().freeMemory());
    long t0 = System.currentTimeMillis();
    ComboBoxModel model = new DefaultComboBoxModel();
    ComboBoxEditor editor = new ComboBoxEditor() {

        public Component getEditorComponent() {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        public void setItem(Object anObject) {
        }

        public Object getItem() {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        public void selectAll() {
        }

        public void addActionListener(ActionListener l) {
        }

        public void removeActionListener(ActionListener l) {
        }
    };
    JComboBox cb;
    for (int i = 0; i < 1000; i++) {
        cb = new JComboBox();
        cb.setModel(model);
        cb.setEditable(false);
        cb.setEditor(editor);
    }
    long dt = t0 - System.currentTimeMillis();
    System.out.println("dt=" + dt + " ms; " + Runtime.getRuntime().freeMemory());
    System.gc();
    System.out.println("finally " + Runtime.getRuntime().freeMemory());
}
Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • tried with manual gc() but doesn't help. Maybe the ComboBox internally creates some strange references inside with the multiple components of a ComboBox. – David Rousal Jan 21 '12 at 23:26
2

I tried with a simple testcase using the NBTestCase#assertGC method. The benefit of this method is that is prints out the strong references to the object you are checking in case of a memory leak. It also triggers GC by filling the heap with a dumb byte array, forcing the GC to start.

The very simple test case I used

public class ComboBoxMemoryLeak {

  public static void main( String[] args ) {
    EventQueue.invokeLater( new Runnable() {
      @Override
      public void run() {
        List<WeakReference<JComboBox>> references = new ArrayList<WeakReference<JComboBox>>(  );
        JComboBox comboBox;
        for (int i = 0; i < 1000; i++ ){
          comboBox = new JComboBox(  );
          references.add( new WeakReference<JComboBox>( comboBox ) );
        }
        comboBox = null;
        for ( int i = 0, referencesSize = references.size(); i < referencesSize; i++ ) {
          System.out.println( "i = " + i );
          WeakReference<JComboBox> weakReference = references.get( i );
          NbTestCase.assertGC( "Combobox", weakReference );
        }
        System.out.println("No memory leak found");
      }
    } );
  }
}

leading to the following trace on my Mac running with JDK1.6

i = 0
Exception in thread "AWT-EventQueue-0" junit.framework.AssertionFailedError: Combobox:
private static sun.awt.AppContext sun.awt.AppContext.mainAppContext->
sun.awt.AppContext@30f7f540-table->
java.util.HashMap@c324b85-table->
[Ljava.util.HashMap$Entry;@770fba26-[8]->
java.util.HashMap$Entry@63adf08f-value->
java.beans.PropertyChangeSupport@4f1b8540-children->
java.util.Hashtable@2305454a-table->
[Ljava.util.Hashtable$Entry;@4a9a4ba3-[0]->
java.util.Hashtable$Entry@6aed0f19-value->
java.beans.PropertyChangeSupport@23597cac-listeners->
sun.awt.EventListenerAggregate@2f39c244-listenerList->
[Ljava.beans.PropertyChangeListener;@2e2e06bd-[0]->
javax.swing.JViewport$1@2a72cf60-this$0->
javax.swing.JViewport@2b9c1dc4-parent->
javax.swing.JScrollPane@b99f7c6-parent->
com.apple.laf.AquaComboBoxPopup@6699166f-comboBox->
javax.swing.JComboBox@3bc634b9
    at junit.framework.Assert.fail(Assert.java:50)
    at org.netbeans.junit.NbTestCase$4.run(NbTestCase.java:1351)
    at org.netbeans.junit.internal.NbModuleLogHandler.whileIgnoringOOME(NbModuleLogHandler.java:143)
    at org.netbeans.junit.NbTestCase.assertGC(NbTestCase.java:1309)
    at org.netbeans.junit.NbTestCase.assertGC(NbTestCase.java:1285)
    at ComboBoxMemoryLeak$1.run(ComboBoxMemoryLeak.java:32)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:677)
    at java.awt.EventQueue.access$000(EventQueue.java:85)
    at java.awt.EventQueue$1.run(EventQueue.java:638)
    at java.awt.EventQueue$1.run(EventQueue.java:636)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:647)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:296)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:211)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:196)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:188)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

So yes, on my machine I would conclude the JComboBox is still present in memory through a reference in the AppContext. Searching on that leads us to this SO question. I tried connecting JConsole to a main method with a while loop with your statement. Unfortunately my main program threw an OutOfMemoryException before JConsole could connect, so I couldn't generate a nice picture like trashgod did in his answer.

Community
  • 1
  • 1
Robin
  • 36,233
  • 5
  • 47
  • 99
1
i can do this code where ever i wont the 1000 ComboBoxes are never GCed 
and i do not get it, why

answer is maybe very simle Object could be GC'ed if there doesn't exist another reference to this Object, or another issue could be that you referenced from/to the static Object (never GC'ed only can take null value), or yours Objects are static

mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • i have no reference to the objects created. maybe swing is keeping some inside – David Rousal Jan 22 '12 at 00:34
  • It kinda sucks as far as getting assertGC to pass, but its a handy tool. As you are nulling each object that has the offending reference, you could end up nulling out five different items unfortunately. I have even tested JPanels with assertGC and I end up having libraries that caused the test to fail. I came up with a solution to solve this but it was ugly and I had to use reflection.... – yams Sep 04 '13 at 16:33
  • @Mark Basler JComboBox and its derived JList has performance issue in the case that is removed more that 999 items, I don't know why, what, where, how .... – mKorbel Sep 04 '13 at 17:04
  • Yeah I have had the same issue with JComboBox, JSplitPane, and JPanels. Also with Listeners as well. These are old issues with Java that have been around for 15years. – yams Sep 04 '13 at 17:22