Duplicate :
|
|
Relates :
|
|
Relates :
|
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) ======================================================================
|