JDK-6257260 : Memory leak on closing JFrame
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 5.0,6
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: linux,linux_redhat_3.0,solaris_10
  • CPU: x86
  • Submitted: 2005-04-19
  • Updated: 2011-02-16
  • Resolved: 2005-08-21
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
5.0u6Fixed 6 b49Fixed
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.5.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-b64)
Java HotSpot(TM) Client VM (build 1.5.0-b64, mixed mode, sharing)


ADDITIONAL OS VERSION INFORMATION :
Linux 2.6.5-mb1 #2 Tue Apr 20 13:22:26 IST 2004 i686 i686 i386 GNU/Linux

A DESCRIPTION OF THE PROBLEM :
About 300 bytes are lost each time the JFrame in the sample code below
is disposed (in addition to the few accounted for by bug 6196089).

Test code adapted from application.
Perhaps not normally not much of an issue, but cumulative in an application
where windows are frequently opened and closed.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Using the test code, create and dispose of a set of 100 windows, say, 6 times.
Then garbage collect a few times. No matter how often it is done, you will
notice in hprof output either 600 or 601 (601 because of the first window)
instances of Hashtable$Entry, PhantomReference, and DefaultDisposerRecord
which are allocated in a variety of places are never collected, e.g.,

	sun.java2d.DisposerRecord.<init>(DisposerRecord.java:14)
	sun.java2d.DefaultDisposerRecord.<init>(DefaultDisposerRecord.java:18)
	sun.java2d.Disposer.addRecord(Disposer.java:69)
	sun.awt.X11Renderer.XCreateGC(X11Renderer.java:Unknown line)
	sun.awt.X11Renderer.<init>(X11Renderer.java:59)
	sun.awt.X11Renderer.getInstance(X11Renderer.java:51)
	sun.awt.X11SurfaceData.makePipes(X11SurfaceData.java:501)
	sun.awt.X11SurfaceData$X11WindowSurfaceData.<init>(X11SurfaceData.java:512)
	sun.awt.X11SurfaceData.createData(X11SurfaceData.java:322)
	sun.awt.X11GraphicsConfig.createSurfaceData(X11GraphicsConfig.java:380)
	sun.awt.X11.XWindow.validateSurface(XWindow.java:872)
	sun.awt.X11.XDecoratedPeer.setBounds(XDecoratedPeer.java:412)
	java.awt.Component.reshapeNativePeer(Component.java:1924)
	java.awt.Component.reshape(Component.java:1880)
	java.awt.Component.setBounds(Component.java:1847)
	java.awt.Window.setBounds(Window.java:2334)
	java.awt.Window.setClientSize(Window.java:494)
	java.awt.Window.pack(Window.java:480)


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Amount of memory after garbage collection should remain fairly constant.
ACTUAL -
About 30k lost each time 100 windows created and disposed.

REPRODUCIBILITY :
This bug can be reproduced always.

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

public class LeakWindow
   extends JFrame
{
   private JTable table;
   protected JScrollPane tablePane;
   private JMenu menu1;

   protected LeakWindow()
   {
      super("Leak Demo");

      TableModel dataModel = new AbstractTableModel()
         {
            public int getColumnCount() { return 10; }
            public int getRowCount() { return 10;}
            public Object getValueAt(int row, int col)
            {
               return new Integer(row*col);
            }
         };
      this.table = new JTable(dataModel);
      this.tablePane = new JScrollPane(this.table);
      this.table.setSize(this.table.getPreferredSize());
      JViewport vp = this.tablePane.getViewport();
      vp.setPreferredSize(new Dimension(
         (int) vp.getPreferredSize().getWidth(),
         (int) this.table.getPreferredSize().getHeight()));

      this.menu1 = new JMenu("Menu");
      JMenuItem menuItem1 = new JMenuItem("100 Windows");
      this.menu1.add(menuItem1);
      menuItem1.addActionListener(new ActionListener()
         {
            public void actionPerformed(ActionEvent e)
            {
               for (int i = 0; i < 100; ++i)
               {
                  LeakWindow win = new
                     LeakWindow();
                  win.show();
                  win.dispose();
               }
            }
         });
      JMenuItem menuItem2 = new JMenuItem("Garbage Collect");
      this.menu1.add(menuItem2);
      menuItem2.addActionListener(new ActionListener()
         {
            public void actionPerformed(ActionEvent e)
            {
               System.out.println("before: "
                  + (Runtime.getRuntime().totalMemory()
                  - Runtime.getRuntime().freeMemory()));
               Runtime.getRuntime().gc();
               System.out.println("after:          "
                  + (Runtime.getRuntime().totalMemory()
                  - Runtime.getRuntime().freeMemory()));
            }
         });
      JMenuBar menuBar = new JMenuBar();
      menuBar.add(this.menu1);
      JPanel menuPanel = new JPanel(new BorderLayout());
      menuPanel.add(menuBar, BorderLayout.CENTER);

      setDefaultCloseOperation(DISPOSE_ON_CLOSE);

      Container cp = getContentPane();
      JPanel topPanel = new JPanel(new BorderLayout());
      topPanel.add(this.tablePane, BorderLayout.CENTER);
      topPanel.add(menuPanel, BorderLayout.NORTH);
      cp.add(topPanel, BorderLayout.NORTH);
      pack();
   }
   public static void main(String args[])
   {
      LeakWindow first = new LeakWindow();
      first.setDefaultCloseOperation(EXIT_ON_CLOSE);
      first.show();
   }
}

---------- END SOURCE ----------
###@###.### 2005-04-19 10:07:06 GMT

Comments
EVALUATION I've created: 6368950: Memory leak when closing a Frame with icon set
04-01-2006

EVALUATION This is a response to the comments about the bug still being reproducible with the [partly] backported version of the fix to 5.0u6. It appears that the customer who made the comments is not the original sumbitter, and is referring to an apparently different problem that has to do with setting an Icon for the frame. The original bug report specifically pointed out the offending stack trace which involved the Java2D disposer, and this problem was addressed with the backport of the original fix. I had a look at the customer's test case (listed at the comments section) and I can reproduce the problem with 5.0u6, but it's a completely different bug, so I'll file a separate bug against AWT.
04-01-2006

EVALUATION Upon more careful examination, the fact that the method is static _is_ the bug =), so the bug is present in 5.0 and needs to be backported.
14-09-2005

EVALUATION There was a question about backporting this fix to 5.0. It appears that both part of the fix for this bug in 6.0 are not relevant to 5.0 - the XCreateGC method is static in 5.0 as it supposed to be, for example. So the fix for 5.0 will require a new investigation.
13-09-2005

EVALUATION The problem in XNETProtocol can be fixed by storing state value in the peer rather than by keeping a separate map. This AWT fix is just the removing the separate map (created for performance improvement) since the cached state values are already saved in the window peer. After this change there is no any XFramePeer objects which are never collected like before.
26-07-2005

EVALUATION There are two problems here. First, there's a leak of GCs created in X11Renderer. The GCs are supposed to be released when an instance of X11Renderer the GC is associated with goes away. Unfortunately insead of an instance of X11Renderer we use the X11Renderer class as our referer. This is because the XCreateGC method is static, and we pass the jobject xr in the native code to the disposer, but this is actually a jclass, so the DisposerRecord uses class as reference. Since the X11Renderer class never goes away, we leak a GC per surface. Unfortunately even when this is corrected we still don't release the GCs, and this time the reason the leak in XAWT's XNETProtocol class. In XNETProtocol.getState() a window is placed into a HashMap, but it never appears to be removed from the map. XWindowPeer (through a couple of indirections) holds a reference to a SurfaceData, which has a reference to an X11Renderer. The latter is used as a referent to a DisposerRecord for the XGC associated with the XRenderer, which is created in X11Renderer's constructor. So we're leaking the SurfaceData itself (and the the associated window), and the GC as well. OBJ 500384e7 (sz=32, trace=306354, class=sun.java2d.x11.X11Renderer@50004c18) OBJ 500384e2 (sz=72, trace=308501, class=sun.java2d.x11.X11SurfaceData$X11WindowSurfaceData@50004c06) surfaceType 50001fa5 colorModel 50004b6e disposerReferent 500384e3 peer 50038302 graphicsConfig 50002392 solidloops 50004c0a x11pipe 500384e7 // this is the reference to X11Renderer object x11txpipe 500384eb OBJ 50038302 (sz=128, trace=306426, class=sun.awt.X11.XContentWindow@50004c4e) children 50038303 parentWindow 5003829d state_lock 50038306 graphicsConfig 50002392 graphicsConfigData 50038308 target 50037dfa surfaceData 500384e2 // this is a reference to the surface data object paintArea 50038315 backgroundColor 50002b9f parentFrame 5003829d iconifiedExposeEvents 5003834d OBJ 5003829d (sz=288, trace=306174, class=sun.awt.X11.XFramePeer@50004599) font 500382b0 insets 5003864f winAttr 500382b4 mwm_hints 500385da XA_NET_WM_STATE 50002823 net_wm_state 500387c0 dimensions 50038734 content 50038302 // reference to the XContentWindow object currentInsets 5003864e focusProxy 50038358 undecorated 50005668 OBJ 50038617 (sz=24, trace=308612, class=java.util.HashMap$Entry@50000099) key 5003829d // reference to the XFramePeer, never released value 50001085 And here's the trace where this hashmap entry is created: This is the stack trace: TRACE 308612: java.util.HashMap$Entry.<init>(HashMap.java:673) java.util.HashMap.addEntry(HashMap.java:743) java.util.HashMap.put(HashMap.java:395) sun.awt.X11.XNETProtocol.getState(XNETProtocol.java:113) sun.awt.X11.XWM.getExtendedState(XWM.java:1027) sun.awt.X11.XWM.isStateChange(XWM.java:1078) We'll deal with the GC leak in 2D code and then pass it to awt folks so they can address theirs. ###@###.### 2005-07-13 17:37:44 GMT I've integrated the 2D part of the fix, reassigning to AWT for further evaluation. ###@###.### 2005-07-14 00:13:07 GMT 2D fix is integrated in b45
13-07-2005