JDK-5071220 : JComboBox does not release memory
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.4.2,6
  • Priority: P4
  • Status: Closed
  • Resolution: Cannot Reproduce
  • OS: generic,windows_2000
  • CPU: generic,x86
  • Submitted: 2004-07-02
  • Updated: 2006-02-14
  • Resolved: 2006-02-14
Related Reports
Relates :  
Description
Name: jl125535			Date: 07/02/2004


FULL PRODUCT VERSION :
java version "1.5.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta3-b57)
Java HotSpot(TM) Client VM (build 1.5.0-beta3-b57, mixed mode)

java version "1.4.2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2-b28)
Java HotSpot(TM) Client VM (build 1.4.2-b28, mixed mode)


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

A DESCRIPTION OF THE PROBLEM :
We have noticed that our application appears to leak memory when a large number of JComboBox components are created in a single session.  The test case below isolates this behavior.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the test case below:

java MemoryLeakTest javax.swing.JComboBox

This will create large numbers of combo box components, then run GC, and display memory usage, and then repeat.  Each time it prints the memory used, and change from the previous iteration.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
After the first few iterations, we expected no change in memory used (or small changes that even out over time).  When run with other components, memory usage is flat, or bounces up and down in small incremements:

C:\temp\mem>java MemoryLeakTest javax.swing.JTextField
Used(0) = 305912
Used(1) = 280792 (-25120)
Used(2) = 281376 (584)
Used(3) = 280792 (-584)
Used(4) = 281376 (584)
Used(5) = 280792 (-584)
Used(6) = 281376 (584)
Used(7) = 280792 (-584)
Used(8) = 281376 (584)
Used(9) = 280792 (-584)
Used(10) = 281376 (584)
Used(11) = 280792 (-584)
Used(12) = 281448 (656)
Used(13) = 280864 (-584)
Used(14) = 281448 (584)
Used(15) = 280864 (-584)
Used(16) = 281448 (584)
Used(17) = 280864 (-584)
Used(18) = 281448 (584)
Used(19) = 280864 (-584)
Used(20) = 281448 (584)
Used(21) = 280864 (-584)
Used(22) = 281448 (584)
Used(23) = 280864 (-584)
Used(24) = 281448 (584)
Used(25) = 280864 (-584)
Used(26) = 281448 (584)
Used(27) = 280864 (-584)
Used(28) = 281448 (584)
Used(29) = 280864 (-584)
Used(30) = 281448 (584)

C:\temp\mem>java MemoryLeakTest java.util.Vector
Used(0) = 90424
Used(1) = 89800 (-624)
Used(2) = 89800 (0)
Used(3) = 89800 (0)
Used(4) = 89800 (0)
Used(5) = 89800 (0)
Used(6) = 89800 (0)
Used(7) = 89800 (0)
Used(8) = 89800 (0)

C:\temp\mem>java MemoryLeakTest java.lang.Object
Used(0) = 90208
Used(1) = 89800 (-408)
Used(2) = 89800 (0)
Used(3) = 89800 (0)
Used(4) = 89800 (0)
Used(5) = 89800 (0)
Used(6) = 89800 (0)
Used(7) = 89800 (0)
Used(8) = 89800 (0)
Used(9) = 89800 (0)
Used(10) = 89800 (0)
Used(11) = 89800 (0)
Used(12) = 89872 (72)
Used(13) = 89872 (0)
Used(14) = 89872 (0)
Used(15) = 89872 (0)
Used(16) = 89872 (0)

C:\temp\mem>java MemoryLeakTest javax.swing.JButton
Used(0) = 220752
Used(1) = 201624 (-19128)
Used(2) = 201880 (256)
Used(3) = 201624 (-256)
Used(4) = 201880 (256)
Used(5) = 201624 (-256)
Used(6) = 201880 (256)
Used(7) = 201624 (-256)
Used(8) = 201880 (256)
Used(9) = 201624 (-256)
Used(10) = 201880 (256)
Used(11) = 201624 (-256)
Used(12) = 201952 (328)
Used(13) = 201696 (-256)
Used(14) = 201952 (256)
Used(15) = 201696 (-256)

C:\temp\mem>java MemoryLeakTest javax.swing.JDialog
Used(0) = 701888
Used(1) = 412976 (-288912)
Used(2) = 419552 (6576)
Used(3) = 419552 (0)
Used(4) = 419552 (0)
Used(5) = 419552 (0)
Used(6) = 419552 (0)
Used(7) = 419552 (0)
Used(8) = 419552 (0)
ACTUAL -
When run with JComboBox, the usage containues to grow (at an average of around 8k per iteration):


C:\temp\mem>java MemoryLeakTest javax.swing.JComboBox
Used(0) = 397040
Used(1) = 362720 (-34320)
Used(2) = 412400 (49680)
Used(3) = 422168 (9768)
Used(4) = 389080 (-33088)
Used(5) = 393960 (4880)
Used(6) = 449504 (55544)
Used(7) = 404304 (-45200)
Used(8) = 394208 (-10096)
Used(9) = 404352 (10144)
Used(10) = 412352 (8000)
Used(11) = 417552 (5200)
Used(12) = 424256 (6704)
Used(13) = 433936 (9680)
Used(14) = 442264 (8328)
Used(15) = 450264 (8000)
Used(16) = 493408 (43144)
Used(17) = 466232 (-27176)
Used(18) = 474984 (8752)
Used(19) = 482984 (8000)
Used(20) = 490224 (7240)
Used(21) = 498224 (8000)
Used(22) = 506976 (8752)
Used(23) = 514656 (7680)
Used(24) = 522984 (8328)
Used(25) = 530984 (8000)
Used(26) = 536936 (5952)
Used(27) = 546544 (9608)
Used(28) = 552216 (5672)
Used(29) = 561504 (9288)
Used(30) = 569504 (8000)
Used(31) = 577304 (7800)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javax.swing.*;
import javax.swing.plaf.basic.BasicComboPopup;

public class MemoryLeakTest {
    public MemoryLeakTest(int count, Class cls) {
        Object obj = null;
        for (int i = 0; i < count; i++) {
            try {
                obj = cls.newInstance();
            } catch (InstantiationException e1) {
            } catch (IllegalAccessException e1) {
            }
        }
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
        }
    }
    
    public static void main(String[] x) {
        if(x.length < 1) {
            System.out.println("USAGE: java MemoryLeakTest <class>");
            return;
        }
        
        try {
            Class classForTest = Class.forName(x[0]);
            long used = 0;
            for (int i = 0; i < 300; i++) {
                System.gc();
                new MemoryLeakTest(1000, classForTest);
                System.gc();
                long usedThisTime = 
                    Runtime.getRuntime().totalMemory() 
                    - Runtime.getRuntime().freeMemory();
                if (i == 0) {
                    System.out.println("Used("+i+") = "+usedThisTime);
                } else {
                    System.out.println("Used("+i+") = "+usedThisTime
                        +" ("+(usedThisTime - used)+")");
                }
                used = usedThisTime;
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.exit(0);
    }
}

---------- END SOURCE ----------
(Incident Review ID: 232494) 
======================================================================

Comments
EVALUATION As leouser points out, this bug is no longer reproducible in Mustang (current build is b71). In fact, it's also no longer reproducible in 1.5 update builds (latest release is 1.5.0_06). It looks very much like this bug was taken care of by: 6196089 : BasicPopupMenuUI$MenuKeyboardHelper added repeatedly to ChangeListener list which makes sense, given the popup portion of a JComboBox is generally implemented using a JPopupMenu. FWIW, this bug is also reproducible using javax.swing.JPopupMenu. 6196089 was integrated into Mustang b26. This bug is reproducible in b25, but not b26. 6196089 was also backported into 1.5.0_04b03 and again, reproducible in b02, not b03. Thanks again to leouser for noticing that we can lay this bug to rest.
14-02-2006

EVALUATION Contribution-Forum:https://jdk-collaboration.dev.java.net/servlets/ProjectForumMessageView?messageID=11419&forumID=1463
13-02-2006

EVALUATION The test case runs as described with 1.5 on 1.4.2 on Solaris. I ran the test through a profiler, but didn't see any obvious leaks. ###@###.### 2004-07-13
13-07-2004