Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4748141

First JInternalFrame added to a JDesktop causes memory leak

    Details

    • Subcomponent:
    • CPU:
      x86
    • OS:
      windows_2000, windows_xp

      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)
      ======================================================================

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                mbronsonsunw Mike Bronson (Inactive)
                Reporter:
                jkimsunw Jeffrey Kim (Inactive)
              • Votes:
                0 Vote for this issue
                Watchers:
                1 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved:
                  Imported:
                  Indexed: