JDK-6511568 : JFileChooser throws OOM in 1.4.2, 5.0u4 and 1.6.0
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 5.0,5.0u12,6u3
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic,windows_xp
  • CPU: generic,x86
  • Submitted: 2007-01-10
  • Updated: 2012-03-22
  • Resolved: 2007-03-27
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other JDK 6 JDK 7
1.4.2_15Fixed 6u4Fixed 7Resolved
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
JFileChooser seems to cause OutOfMemory.

CONFIGURATION:
 -OS  : winXP(SP2,Japanese)
 -JRE : 1.4.2_11, 5.0u4, mustangb75

REPRODUCE:
 1) Compile the attached test program, JFileChooserTest.java
 2) Launch 
       java -Xmx8m -verbose:gc JFileChooserTest > log 2>&1

 3) A dialogue appears, 
    if you use 1.4.2_XX, please click go button.
    if 5.0ux, please set 200 to text box of "repeat count" 
   Then Click "Go" BUtton.

 4) when the program stopped, please refer to log file.
    OutOfMemory message appears.
  

NOTE:
  - -Xmx8m is specified to reproduce quickly.
     Actual licnesee program does not set such small heap size.
  - When we use JFileChose, updateUI() might not be applied.
    This is speficied to show the possibility of some leak in JFIleChooser.
    That's specified  only for quick reproduction.

Comments
SUGGESTED FIX The above suggested fix has caused some serious regression, so we have reworked on this fix. New fix is done is JFileChooser class. New fix is to call dialog.removeAll(); from the showDialog function before the dialog is disposed.
21-03-2007

SUGGESTED FIX the proposed fix is quite simple: to prevent component from registering in KeyboardManager if it's not showing. Most likely it would be done in JComponent's method private void registerWithKeyboardManager(boolean onlyIfNew) with additional check like this: if (!isShowing()) { return; }
10-01-2007

EVALUATION Evaluation Done be Leonid <###@###.###> I have investigated the issue and found the root cause why instances of JDialog don't get GC'ed. Actually it is KeyboardManager who holds live references to them. I'll try to explain how. In the test, we create a visual form (e.g. JFileChooser) that we put into a JDialog. JFileChooser in Metal and Windows LaF's has two or three JLabels binded with other components through 'setLabelFor' method, and having key mnemonic set via 'setDisplayedMnemonic'. On dialog creation, it registers the bindings in KeyboardManager, here is the call stack: registerKeyStroke():83, javax.swing.KeyboardManager registerWithKeyboardManager():2163, javax.swing.JComponent registerWithKeyboardManager():2088, javax.swing.JComponent addNotify():4668, javax.swing.JComponent addNotify():2579, java.awt.Container addNotify():4665, javax.swing.JComponent addNotify():2579, java.awt.Container addNotify():4665, javax.swing.JComponent addNotify():2579, java.awt.Container addNotify():4665, javax.swing.JComponent addNotify():2579, java.awt.Container addNotify():4665, javax.swing.JComponent addNotify():2579, java.awt.Container addNotify():4665, javax.swing.JComponent addNotify():735, javax.swing.JRootPane addNotify():2579, java.awt.Container addNotify():635, java.awt.Window addNotify():744, java.awt.Dialog pack():663, java.awt.Window createDialog():780, javax.swing.JFileChooser showDialog():714, javax.swing.JFileChooser showOpenDialog():626, javax.swing.JFileChooser doAttempt():65, TwentyThousandTest main():37, TwentyThousandTest When registering a keystroke, KeyboardManager puts the top parent window (JDialog in our case) in the 'containerMap' hashmap. After the dialog closes, the key bindings get unregistered: unregisterKeyStroke():170, javax.swing.KeyboardManager unregisterWithKeyboardManager():2167, javax.swing.JComponent unregisterWithKeyboardManager():2137, javax.swing.JComponent removeNotify():4687, javax.swing.JComponent removeNotify():2606, java.awt.Container removeNotify():4681, javax.swing.JComponent removeNotify():2606, java.awt.Container removeNotify():4681, javax.swing.JComponent removeNotify():2606, java.awt.Container removeNotify():4681, javax.swing.JComponent removeNotify():2606, java.awt.Container removeNotify():4681, javax.swing.JComponent removeNotify():2606, java.awt.Container removeNotify():4681, javax.swing.JComponent removeNotify():750, javax.swing.JRootPane removeNotify():2606, java.awt.Container removeNotify():645, java.awt.Window run():973, java.awt.Window$1DisposeAction dispatch():199, java.awt.event.InvocationEvent dispatchEvent():597, java.awt.EventQueue pumpOneEventForFilters():273, java.awt.EventDispatchThread pumpEventsForFilter():183, java.awt.EventDispatchThread pumpEventsForHierarchy():173, java.awt.EventDispatchThread pumpEvents():168, java.awt.EventDispatchThread pumpEvents():160, java.awt.EventDispatchThread run():121, java.awt.EventDispatchThread The JDialog gets removed from the map, and everything is fine so far. But later on, for the next iteration (or attempt), if 'UPDATE_UI_EACH_INTERVAL' set to 'true' we call 'updateUI' on JFileChooser. If we haven't just created a new instance of JFileChooser ('ALWAYS_NEW_INSTANCE' was set to 'false'), it still refers to the already closed JDialog as its parent. So this causes the key bindings to register again through another call stack: registerKeyStroke():83, javax.swing.KeyboardManager registerWithKeyboardManager():2163, javax.swing.JComponent registerWithKeyboardManager():2088, javax.swing.JComponent componentInputMapChanged():2158, javax.swing.JComponent put():77, javax.swing.ComponentInputMap installKeyboardActions():363, javax.swing.plaf.basic.BasicLabelUI propertyChange():412, javax.swing.plaf.basic.BasicLabelUI firePropertyChange():339, java.beans.PropertyChangeSupport firePropertyChange():276, java.beans.PropertyChangeSupport firePropertyChange():7846, java.awt.Component setLabelFor():1014, javax.swing.JLabel installComponents():221, javax.swing.plaf.metal.MetalFileChooserUI installUI():136, javax.swing.plaf.basic.BasicFileChooserUI installUI():124, javax.swing.plaf.metal.MetalFileChooserUI setUI():668, javax.swing.JComponent updateUI():1762, javax.swing.JFileChooser doAttempt():56, TwentyThousandTest main():37, TwentyThousandTest An instance of JDialog (the chooser's parent) gets stored in the KeyboardManager's 'containerMap' again and lives there forever, because on the next step we create and open a new JDialog for JFileChooser, and the cycle repeats. So every iteration leaves an instance of JDialog in the 'containerMap'. In turn, KeyboardManager has a static field 'currentManager' where it keeps an instance of itself so the 'containerMap' also doesn't get collected by GC, and hundreds of JDialogs fill up the memory. That's all! As for possible ways of fixing, I think, since we cannot reopen an already closed JFileChooser's dialog, it seems to be a good idea to remove all components on dialog closing. It will prevent the leak from appearing in real applications, although it is quite unlikely to happen as it is, to my mind.
10-01-2007