JDK-4748141 : First JInternalFrame added to a JDesktop causes memory leak
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.4.0,1.4.1
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_2000,windows_xp
  • CPU: x86
  • Submitted: 2002-09-16
  • Updated: 2015-01-23
  • Resolved: 2005-09-19
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
Name: jk109818			Date: 09/16/2002


FULL PRODUCT VERSION :
java version "1.4.1-rc"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-rc-b19)
Java HotSpot(TM) Client VM (build 1.4.1-rc-b19, mixed mode)

FULL OPERATING SYSTEM VERSION :

Microsoft Windows 2000 [Version 5.00.2195]
Service Pack 2

A DESCRIPTION OF THE PROBLEM :
I seem to have found a memory leak with JDesktop and
JInternalFrame on JDK1.4.x.

If you run the sample application below under a heap
profiler, and close all of
the internal frames, you will see that one of the internal
frames is never
destroyed.  The order of closing doesn't seem to matter.
It's always the first
frame added to the desktop ("internal0") that is leaked.
Note that the internal
frame's dispose() method *is* called -- I verified this
under the debugger -- yet
still somebody is holding a reference.

I can also reproduce this bug with
the "InternalFrameEventDemo.java" application
from sun's tutorial site (with the DisplayWindow modified
to be closeable).


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Compile and run the sample application below, under a
heap profiler
2. Close all of the internal frames, in any order
3. Inspect objects under the profiler.  One of the
JInternalFrames (the first to be added to the desktop) was
not destroyed and still has live back-references.

EXPECTED VERSUS ACTUAL BEHAVIOR :
All of the JInternalFrames should have been destroyed.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.Dimension;

import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;


public class Main {

 public static void main(String[] args) {

  JFrame frame = new JFrame("frame");
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

  JDesktopPane desktop = new JDesktopPane();
  desktop.setPreferredSize(new Dimension(400,400));
  frame.setContentPane(desktop);

  for (int i = 0; i < 5; i++) {

   JInternalFrame internal = new JInternalFrame("internal"+i, true, true);
   internal.setPreferredSize(new Dimension(200,200));
   internal.setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);

   desktop.add(internal);

   internal.pack();
   internal.show();
  }

  frame.pack();
  frame.show();
 }
}


---------- END SOURCE ----------

CUSTOMER WORKAROUND :
I don't know of a workaround, but you can at least reduce
the severity of the leak.  Since the JInternalFrame's
dispose() method *is* called, an application subclassing
the internal frame can release its object references at
that point (to avoid leaking everything the frame holds
onto along with the frame).
(Review ID: 164068) 
======================================================================

Name: jk109818			Date: 09/25/2002


FULL PRODUCT VERSION :
java version "1.4.0_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0_01-b03)
Java HotSpot(TM) Client VM (build 1.4.0_01-b03, mixed mode)

FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2915]

ADDITIONAL OPERATING SYSTEMS :
Redhat Linux 7.3



A DESCRIPTION OF THE PROBLEM :
Using any Look and Feel Derived from BasicLookAndFeel the
first JInternalFrame is always referenced by the UIManager.

The BasicInternalFrameUI lazely creates an ActionMap
 for the UIManager until the first BasicInternalFrameUI
installUI is called and then adds the action map to the
UIManager defaults table. In the creation of the map
BasicInternalFrameUI creates an ananymous inner class and
adds the inner class as a value in the ActionMap for the
key "showSystemMenu".  Subsequent calls to installUI use
the default map stored in UIManager.

1. The UIManager defaults table (a static member of
UIManager) holds a refernce to the anonymous inner class
created in BasicInternalFrameUI.createActionMap
2. The inner class holds a reference to the
BasicInternalFrameUI due to it being an inner class
4. All BasicInternalFrameUIs hold a reference to its
JInternalFrame via its frame member.
5. The first JInternalFrame that called installUI for
itself is referenced via the static defaults table of
UIManager and is never GCed.

Following code is from BasicInternalFrameUI

public void installUI(JComponent c)   {
    frame = (JInternalFrame)c;
    installDefaults();
    installListeners();
    installComponents();
    installKeyboardActions();

    frame.setOpaque(true);
}

protected void installKeyboardActions(){
    if (internalFrameListener == null)
        createInternalFrameListener();
        frame.addInternalFrameListener
(internalFrameListener);

        ActionMap actionMap = getActionMap();
        SwingUtilities.replaceUIActionMap(frame, actionMap);
}

ActionMap getActionMap() {
    ActionMap map = (ActionMap)UIManager.get
("InternalFrame.actionMap");
    if (map == null) {
        map = createActionMap();
        if (map != null) {
	    UIManager.getLookAndFeelDefaults().put
("InternalFrame.actionMap",
						   map);
        }
    }
    return map;
}

ActionMap createActionMap() {
    ActionMap map = new ActionMapUIResource();
    // add action for the system menu
    map.put("showSystemMenu", new AbstractAction(){
        public void actionPerformed(ActionEvent e){
	    titlePane.showSystemMenu();
	}
        public boolean isEnabled(){
	  return isKeyBindingActive();
	}
     });
     // Set the ActionMap's parent to the Auditory Feedback
Action Map
     BasicLookAndFeel lf = (BasicLookAndFeel)
UIManager.getLookAndFeel();
    ActionMap audioMap = lf.getAudioActionMap();
    map.setParent(audioMap);
    return map;
}

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a JInternalFrame with no references
          new JInternalFrame();
2. Use OptimizeIt (or any other debugging tool) to see if
   the above created JInternalFrame is destroyed.

Can also add a finalizer to JInternalFrame to see if the
   frame is GCed for step 1

EXPECTED VERSUS ACTUAL BEHAVIOR :
The first JInternalFrame would get GCed when all non-swing
references are removed.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javax.swing.*;

public class TestInternalFrame {
	static public void main(String [] args) {
		new JInternalFrame() {
			public void finalize() { System.out.println("First
Finialized"); }
		};
		System.gc();
		new JInternalFrame() {
			public void finalize() { System.out.println("Second
Finialized"); }
		};
		System.gc();
		new JInternalFrame() {
			public void finalize() { System.out.println("Third
Finialized"); }
		};
		System.gc();
	}
}

---------- END SOURCE ----------

CUSTOMER WORKAROUND :
Create a JInternalFrame early (after the LookAndFeel is
set) in the application that will not hold references to
any other objects.
(Review ID: 164966)
======================================================================

Name: jk109818			Date: 02/24/2003


FULL PRODUCT VERSION :
java version "1.4.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode)

FULL OPERATING SYSTEM VERSION :
Microsoft Windows XP [Version 5.1.2600]
Service Pack 1

ADDITIONAL OPERATING SYSTEMS :
Also on Windows 2000, unknown service pack


A DESCRIPTION OF THE PROBLEM :
The first JInternalFrame created (via new
JInternalFrame(..)) never gets garbage collected.  Frames
created after the first one do get collected normally.

The example program demonstrates this using an extension of
JInternalFrame with a printout in the finalize() method. Two
frames are created and then both are set back to null.
System.gc() is then called to request collection. The second
frame is collected then, but the first is not.

For an added test, I then ran the system out of memory to
prove that it is not getting collected. The first frame
never gets finalized before the application throws and
OutOfMemoryError.

While this depends on printouts in the finalize method, the
same results can also be seen by using the "hprof" heap
profiler in the JRE. The first JInternalFrame will exist on
the heap at program exit.

While this is not a terrible problem for straight use of
JInternalFrames, use of any extension thereof that retains
references to high-memory footprint objects will create
great frustration without applying the workaround.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Compile and run the test program included, or inspect the
heap on any program that creates JInternalFrame objects
2. The printouts will show that the first frame is never
finalized but the second one is.

EXPECTED VERSUS ACTUAL BEHAVIOR :
Expected:
Both frames should get finalized and GC'd after the call to
System.gc(). At the very least, they should both be
collected by the time the system runs out of memory.

Actual:
Only the second frame is finalized and GC'd. This can also
be seen on a heap profile.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javax.swing.*;
import java.awt.*;

public class test {

  static class myframe extends JInternalFrame {
    String id;

    protected void finalize() throws Throwable {
      super.finalize();
      System.out.println("Finalizing " + id);
    }

  }

  public static void main(String[] args) throws Exception {
    myframe f1, f2;

    f1 = new myframe();
    f1.id = "First";

    f2 = new myframe();
    f2.id = "Second";
    f1 = null;
    f2 = null;
    System.gc();
    java.util.Vector v = new java.util.Vector();
    int i = 0;
    while (true) {
       v.add("Consume Memory " + i++);
    }
  }

}

---------- END SOURCE ----------

CUSTOMER WORKAROUND :
Create a new JInternalFrame at the beginning of the
application before using any extensions of JInternalFrame
which have references to objects which consume large amounts
of memory.
(Review ID: 181526)
======================================================================

Comments
EVALUATION Not reproducible with 1.6.0-ea-b51 on Windows XP with NetBeans Profiler. We believe this was fixed by the fix for 4836639 Creating components in XP L&F throws OutOfMemoryError. Closed duplicate of 4836639.
19-09-2005

EVALUATION Verified bug is reproducible with build 1.4.1-b21 with test case Main.
10-09-2005

EVALUATION Thank you for the comments in the bug report and the Sun Developer Network for this bug. They appear to be a good analysis of the reported problem. We will investigate. Has anyone seen this problem with 1.6?
09-09-2005

EVALUATION I am receiving strange results from various test cases with internal frames. Sometimes objects are finalized and other times they are not. The only difference between the two test cases are one is all done on the main thread which works correctly, though it is not the correct way to do this. While the other is executed on the event-queue, and fails. ###@###.### 2002-10-18
18-10-2002