United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-6511568 : JFileChooser throws OOM in 1.4.2, 5.0u4 and 1.6.0

Details
Type:
Bug
Submit Date:
2007-01-10
Status:
Resolved
Updated Date:
2012-03-22
Project Name:
JDK
Resolved Date:
2007-03-27
Component:
client-libs
OS:
generic,windows_xp
Sub-Component:
javax.swing
CPU:
generic,x86
Priority:
P2
Resolution:
Fixed
Affected Versions:
5.0,5.0u12,6u3
Fixed Versions:
5.0u12 (b02)

Related Reports
Backport:
Backport:
Backport:
Duplicate:
Duplicate:
Duplicate:
Duplicate:
Relates:
Relates:
Relates:

Sub Tasks

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.
                                     
2007-03-21
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;
}
                                     
2007-01-10
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.
                                     
2007-01-10



Hardware and Software, Engineered to Work Together