JDK-6559589 : Memory leak in JScrollPane.updateUI()
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 6
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2007-05-18
  • Updated: 2017-05-16
  • Resolved: 2009-05-18
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.
JDK 6 JDK 7
6u14 b02Fixed 7Fixed
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)

java version "1.5.0_11"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_11-b03)
Java HotSpot(TM) Client VM (build 1.5.0_11-b03, mixed mode, sharing)


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

Linux rh3 2.4.21-32.ELsmp #1 SMP Fri Apr 15 21:17:59 EDT 2005 i686 i686 i386 GNU/Linux


A DESCRIPTION OF THE PROBLEM :
In default L&F (metal), each call of JScrollPane.updateUI() will make it's propertyChangeListener list grow.
The leaked class is: javax.swing.plaf.metal.MetalScrollPaneUI$1, which is created in method javax.swing.plaf.metal.MetalScrollPaneUI.createScrollBarSwapListener().

It can be reproduced with both J2SE 1.5 and 1.6, on both Windows and Linux platform.

In my opinion, the root cause should be: javax.swing.plaf.metal.MetalScrollPaneUI extends javax.swing.plaf.basic.BasicScrollPaneUI, but the method "void uninstallListeners(JScrollPane scrollPane)" in MetalScrollPaneUI does not override the method "void uninstallListeners(JComponent c)" in BasicScrollPaneUI, so the listener registered in MetalScrollPaneUI does not be unregistered when uninstallUI is called.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
It is simple to reproduce. In any Swing program, call updateUI() method of a JScrollPane object, and then check it's property change listener list with method getPropertyChangeListeners(), you will see the listener list grows each time.
In my sample source code, it also print out the class name of the listeners.



REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package test;

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.beans.PropertyChangeListener;
import java.util.*;

public class UpdateTest extends JDialog {
  javax.swing.Timer timer;
  JButton exitBtn = new JButton();
  JScrollPane jScrollPaneTest = new JScrollPane();
  JLabel jLabelInfo = new JLabel();

  public static void main(String[] args) {
    UpdateTest dlg = new UpdateTest();
    dlg.setVisible(true);
  }

  public UpdateTest() {

    setTitle("UpdateTest");

    exitBtn.setText("exit");
    exitBtn.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        System.exit(0);
      }
    });
    jLabelInfo.setText("Test JScrollPane.updateUI, please see stdout.");
    jScrollPaneTest.getViewport().add(jLabelInfo);

    getContentPane().setLayout(new BorderLayout());
    getContentPane().add(exitBtn, java.awt.BorderLayout.SOUTH);
    getContentPane().add(jScrollPaneTest, java.awt.BorderLayout.CENTER);
    pack();

    timer = new javax.swing.Timer(1000, new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        JComponent toUpdate = jScrollPaneTest;

        toUpdate.updateUI();
        PropertyChangeListener[] listeners = toUpdate.getPropertyChangeListeners();

        // print it's PropertyChangeListeners after updateUI.
        System.out.println("There are " + listeners.length + " PropertyChangeListeners.");
        HashMap<String, Integer> h = new HashMap<String, Integer>();
        for (int i = 0; i < listeners.length; ++i) {
          String className = listeners[i].getClass().getName();
          int n = 1;
          if (h.containsKey(className)) {
            n = h.get(className) + 1;
          }
          h.put(className, n);
        }
        for (Iterator<Map.Entry<String, Integer>> it = h.entrySet().iterator(); it.hasNext(); ) {
          Map.Entry<String, Integer> entry = it.next();
          System.out.println("" + entry.getValue() + " listeners are " + entry.getKey());
        }
        System.out.println();
      }
    });
    timer.start();
  }
}


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

Comments
EVALUATION MetalScrollPaneUI.uninstallListeners(JScrollPane scrollPane) doesn't override the super method and actually never get called. I agree with the submitter about the fix
05-02-2009