JDK-6575402 : Top-level Memory Leak in BufferStrategyPaintManager
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 6
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2007-06-29
  • Updated: 2021-07-13
Related Reports
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_01"
Java(TM) SE Runtime Environment (build 1.6.0_01-b06)
Java HotSpot(TM) Client VM (build 1.6.0_01-b06, mixed mode, sharing)


ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
There is a memory leak in Swing, whereby it incorrectly retains a reference to a modal JDialog after it has been hidden and disposed of.

Please refer to the test program.  This program displays a JFrame with a panel that has a mouse listener attached it.  When the popup gesture is triggered on the panel, a popup menu is shown, which in turn creates and shows a modal JDialog.  Pressing the close button on the dialog, or pressing the titlebar close button, hides the dialog and disposes of it.

Run the test program under a profiler (I used JProfiler v4.3.1).  Notice that a reference to an instance of DialogOne is retained even after the dialog has been closed and disposed of.  Subsequent launches of the dialog and closing it does not cause further references to be retained, but that initial, single reference is never released.

I am unsure as to where in Swing it retains a reference to the dialog, but it appears to be somewhere in the repaint mechanism.  For comparison, uncomment the line in the test program which adds a JButton to the panel.  Now run the test program under the profiler; notice that now Swing correctly releases the reference to the dialog, after it has been closed.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See above.


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
-- Dialog Test --

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class DialogTest extends JFrame {

  public DialogTest() {
    init();
    validate();
    setVisible(true);
  }

  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        new DialogTest();
      }
    });
  }

  private void init() {
    addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent evt) {
        System.exit(0);
      }
    });

    getContentPane().setLayout(new BorderLayout());
    getContentPane().add(buildMainPanel(), BorderLayout.CENTER);

    setSize(new Dimension(300, 300));
  }

  private JPanel buildMainPanel() {
    JPanel p = new JPanel(new BorderLayout());

    JButton btn = new JButton("Button");
//    p.add(btn, BorderLayout.NORTH);


    p.addMouseListener(new MouseAdapter() {
      public void mouseReleased(MouseEvent e) {
        if (! e.isPopupTrigger()) {
          return;
        }

        JPopupMenu menu = new JPopupMenu();

        JMenuItem item = new JMenuItem("Launch");
        item.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            DialogOne dialog = new DialogOne(DialogTest.this);
            dialog.pack();
            dialog.setLocationRelativeTo(DialogTest.this);
            dialog.setVisible(true);
          }
        });

        menu.add(item);

        menu.show(e.getComponent(), e.getPoint().x, e.getPoint().y);
      }
    });

    return(p);
  }

}

-- end --

-- DialogOne --

import java.awt.event.*;
import javax.swing.*;

public class DialogOne extends JDialog implements ActionListener {

  public DialogOne(JFrame parent) {
    super(parent, "Test", true);
    init();
    setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
  }

  private void init() {
    JButton btn = new JButton("Close");
    btn.addActionListener(this);

    getContentPane().add(btn);
  }

  public void actionPerformed(ActionEvent e) {
    setVisible(false);
    dispose();
  }

}

-- end --
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
I have no work-around.

Comments
EVALUATION You are right - this leak is in the repainting code, and happens for any top-level, not just JDialog (I confirmed with JWindow and JFrame). However this is not a "long term" leak - the reference is cleared the next time a repaint happens. For instance, resizing the DialogTest window will clear the reference. The leak is in the code added for the Gray Rect fix (4967886), and is present in JDKs 6u1 and 7 (as of b16). (If you go back to 6.0, you hit 6462383 instead). So another workaround is to disable 4967886 by running with "-Dswing.bufferPerWindow=false".
27-07-2007

WORK AROUND There are a couple workarounds * force a top-level repaint to happen, such as by resizing a frame * disable the gray rect fix by running with "-Dswing.bufferPerWindow=false"
27-07-2007