JDK-6345050 : JTable got larger in 1.5.0
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 5.0
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2005-11-02
  • Updated: 2017-05-16
  • Resolved: 2006-03-01
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
6 b74Fixed
Related Reports
Relates :  
Relates :  
Description
According to the test case below and -verbose:gc, a simple empty JTable grew significantly over the course of JDK 1.5.  The data below were generated by allocating the indicated number of tables, performing a volley of GCs, and recording the amount of used heap space. 

1.4.2:
0: 314K
10: 362K    (4.8K/per)
100: 800K   (4.8K/per)
1000: 5149K (4.8K/per)

1.5.0:
0: 413K
10: 515K     (10.0K/per)
100: 1418K   (10.1K/per)
1000: 10436K (10.0K/per)

1.6.0b57:
0: 416K
10: 518K     (10.2K/per)
100: 1425K   (10.1K/per)
1000: 10477K (10.1K/per

(FWIW, the 0 number was generated by first allocating 1 table, GCing, then allocating 0 tables and GCing, ensuring that all necessary classes and infrastructure were already loaded).

As you can see, JTable pretty much doubled in size between 1.4.2 and 1.5, though 1.6 appears to have stayed the course.

Test case:
---
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class GCTest {
    private JTextField allocTextField;
    private JTable[] data;

    public static void main(String[] args) {
        new GCTest();
    }

    public GCTest() {
        JFrame frame = new JFrame("GC Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        allocTextField = new JTextField(10);
        allocTextField.setText("0");
        allocTextField.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    int size = Integer.parseInt(allocTextField.getText());
                    System.err.println("allocating: " + size + " tables");
                    data = new JTable[size];
                    for (int i = 0; i < size; i++) {
                        data[i] = new JTable();
                    }
                }
            });
        allocTextField.select(0, 1);
        JButton button = new JButton("GC");
        button.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    System.gc();
                    System.runFinalization();
                    System.gc();
                }
            });

        frame.getContentPane().setLayout(new GridBagLayout());
        frame.getContentPane().add(allocTextField, new GridBagConstraints(
                      0, 0, 1, 1, 1, 0, GridBagConstraints.CENTER,
                      GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0, 0),
                      0, 0));
        frame.getContentPane().add(button, new GridBagConstraints(
                      1, 0, 1, 1, 0, 0, GridBagConstraints.CENTER,
                      GridBagConstraints.NONE, new Insets(5, 5, 0, 5),
                      0, 0));

        frame.pack();
        frame.show();
        allocTextField.requestFocus();
    }
}

Comments
EVALUATION The most direct cause of this bug is one of the changes put in for 4911244 (Reduce init time for UI defaults). Since the UIDefaults for LookAndFeels can get pretty big, 4911244 went ahead and set the initial size of UIDefaults pretty high. This saves a lot of needless time spent resizing the UIDefaults at startup, since the UIDefaults will end up getting big anyway. The problem is that there are a couple places where UIDefaults don't need to be nearly so big. In particular, JTable keeps its default editors and renderers in UIDefaults, both of which have only a few (<10) entries. The fix I'm working with is to add a new constructor to UIDefaults, allowing an initialCapacity and loadFactor to be passed to the parent Hashtable(int initialCapacity, float loadFactor) constructor. This allows all UIDefaults to be constructed of appropriate initial size - large for BasicLookAndFeel, small for JTable, and moderate for Synth. This fixes this bug, and may even save additional footprint as other UIDefaults are "right-sized". I gathered the following data w/ this fix on Solaris 10/x86: 1.4.2 ----- 0: 1236k 100: 1732k (4.96/per) 1000: 6080k (4.84/per) 1.5.0_06 -------- 0: 544k 100: 1558k (10.14k/per) 1000: 10582k (10k/per) 1.6.0 w/ fix ------------ 0: 582k 100: 1052k (4.7/per) 1000: 5236k (4.65/per) So, this fix gives us numbers that are actually a bit better than 1.4.2. :)
03-02-2006

SUGGESTED FIX *** /home/bchristi/jano/bchristi/swing-tablefix/webrev/./src/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java- Thu Feb 2 15:52:51 2006 --- BasicLookAndFeel.java Thu Feb 2 15:50:16 2006 *************** *** 92,102 **** private Clip clipPlaying; AWTEventHelper invocator = null; public UIDefaults getDefaults() { ! UIDefaults table = new UIDefaults(); initClassDefaults(table); initSystemColorDefaults(table); initComponentDefaults(table); --- 92,102 ---- private Clip clipPlaying; AWTEventHelper invocator = null; public UIDefaults getDefaults() { ! UIDefaults table = new UIDefaults(610, 0.75f); initClassDefaults(table); initSystemColorDefaults(table); initComponentDefaults(table); *** /home/bchristi/jano/bchristi/swing-tablefix/webrev/./src/share/classes/javax/swing/plaf/multi/MultiLookAndFeel.java- Thu Feb 2 15:52:53 2006 --- MultiLookAndFeel.java Thu Feb 2 13:34:44 2006 *************** *** 106,116 **** * * @return an initialized <code>UIDefaults</code> object * @see javax.swing.JComponent#getUIClassID */ public UIDefaults getDefaults() { - UIDefaults table = new MultiUIDefaults(); String packageName = "javax.swing.plaf.multi.Multi"; Object[] uiDefaults = { "ButtonUI", packageName + "ButtonUI", "CheckBoxMenuItemUI", packageName + "MenuItemUI", "CheckBoxUI", packageName + "ButtonUI", --- 106,115 ---- *************** *** 154,163 **** --- 153,163 ---- "ToolTipUI", packageName + "ToolTipUI", "TreeUI", packageName + "TreeUI", "ViewportUI", packageName + "ViewportUI", }; + UIDefaults table = new MultiUIDefaults(uiDefaults.length / 2, 0.75f); table.putDefaults(uiDefaults); return table; } /////////////////////////////// *************** *** 275,283 **** --- 275,286 ---- * getUIError method of UIDefaults, which is the method that * emits error messages when it cannot find a UI class in the * LAF. */ class MultiUIDefaults extends UIDefaults { + MultiUIDefaults(int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor); + } protected void getUIError(String msg) { System.err.println("Multiplexing LAF: " + msg); } } *** /home/bchristi/jano/bchristi/swing-tablefix/webrev/./src/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java- Thu Feb 2 15:52:54 2006 --- SynthLookAndFeel.java Thu Feb 2 13:43:22 2006 *************** *** 621,631 **** * Returns the defaults for this SynthLookAndFeel. * * @return Defaults table. */ public UIDefaults getDefaults() { ! UIDefaults table = new UIDefaults(); Region.registerUIs(table); table.setDefaultLocale(Locale.getDefault()); table.addResourceBundle( "com.sun.swing.internal.plaf.basic.resources.basic" ); --- 621,631 ---- * Returns the defaults for this SynthLookAndFeel. * * @return Defaults table. */ public UIDefaults getDefaults() { ! UIDefaults table = new UIDefaults(60, 0.75f); Region.registerUIs(table); table.setDefaultLocale(Locale.getDefault()); table.addResourceBundle( "com.sun.swing.internal.plaf.basic.resources.basic" ); *** /home/bchristi/jano/bchristi/swing-tablefix/webrev/./src/share/classes/javax/swing/JTable.java- Thu Feb 2 15:52:45 2006 --- JTable.java Wed Feb 1 14:02:58 2006 *************** *** 5299,5309 **** * booleans, and icons. * @see javax.swing.table.DefaultTableCellRenderer * */ protected void createDefaultRenderers() { ! defaultRenderersByColumnClass = new UIDefaults(); // Objects setLazyRenderer(Object.class, "javax.swing.table.DefaultTableCellRenderer$UIResource"); // Numbers --- 5299,5309 ---- * booleans, and icons. * @see javax.swing.table.DefaultTableCellRenderer * */ protected void createDefaultRenderers() { ! defaultRenderersByColumnClass = new UIDefaults(8, 0.75f); // Objects setLazyRenderer(Object.class, "javax.swing.table.DefaultTableCellRenderer$UIResource"); // Numbers *************** *** 5406,5416 **** /** * Creates default cell editors for objects, numbers, and boolean values. * @see DefaultCellEditor */ protected void createDefaultEditors() { ! defaultEditorsByColumnClass = new UIDefaults(); // Objects setLazyEditor(Object.class, "javax.swing.JTable$GenericEditor"); // Numbers --- 5406,5416 ---- /** * Creates default cell editors for objects, numbers, and boolean values. * @see DefaultCellEditor */ protected void createDefaultEditors() { ! defaultEditorsByColumnClass = new UIDefaults(3, 0.75f); // Objects setLazyEditor(Object.class, "javax.swing.JTable$GenericEditor"); // Numbers *** /home/bchristi/jano/bchristi/swing-tablefix/webrev/./src/share/classes/javax/swing/UIDefaults.java- Thu Feb 2 15:52:50 2006 --- UIDefaults.java Thu Feb 2 14:07:54 2006 *************** *** 72,82 **** /** * Create an empty defaults table. */ public UIDefaults() { ! super(700, .75f); resourceCache = new HashMap(); } /** --- 72,92 ---- /** * Create an empty defaults table. */ public UIDefaults() { ! this(700, .75f); ! } ! ! /** ! * Create an empty defaults table with the specified initial capacity and ! * load factor. ! * ! * @see java.util.Hasthtable ! */ ! public UIDefaults(int initialCapacity, float loadFactor) { ! super(initialCapacity, loadFactor); resourceCache = new HashMap(); } /** *************** *** 98,108 **** for(int i = 0; i < keyValueList.length; i += 2) { super.put(keyValueList[i], keyValueList[i + 1]); } } - /** * Returns the value for key. If the value is a * <code>UIDefaults.LazyValue</code> then the real * value is computed with <code>LazyValue.createValue()</code>, * the table entry is replaced, and the real value is returned. --- 108,117 ----
03-02-2006

EVALUATION Other interesting data might be: * change in size of table with some data in it * change in size when table is actually shown :) * among different LAFs Another data point: turning sharing off makes no difference for 1.5.
02-11-2005