-
Bug
-
Resolution: Fixed
-
P4
-
1.3.0
-
beta
-
x86
-
windows_nt
Name: skT45625 Date: 05/25/2000
1.3.0-C (HotSpot Client VM 1.3.0-C mixed mode)
We have our Swing app's mem leaks down to near zero (thanks for 1.3's
improvements!) with the exception of JMenuItem objs (& related objs) discussed
in this bug submission. It appears that new JMenuItems (for JInternalFrame
system menu L&Fs) are allocated every time a JInternalFrame is selected. It
can be a user titlebar click selection, or a programmatic one that setSelected
(true)s or toFront()s the JIF. Tracing down, it appears that the JIF's system
menu is rebuilt every time the JIF is selected.
The good news: Most of the JMenuItem related objs seem reachable (not able to
be gced) *only* until the JInternalFrame is closed (w/DISPOSE opt.).
The bad news: If the JIF is not closed, the JMenuItem stuff accumulates until
an out-of-memory condition occurs (the weak ref argument like that in:
http://developer.java.sun.com/developer/bugParade/bugs/4280094.html
does not appear to hold).
Using your favorite heap analyzer (I used OptimizeIT 3.1), run the hastily
constructed example code at end (using -Xms3M & -Xmx5M to show refs are not
released). Use MakeFrame to open a 3-5 JIFs & reset your heap obj counters.
Now click each of the JIF titlebars successively (or use Start Timer button to
spawn a Timer to do this) and watch the heap. The new 1.3 client hotspot gc
thread does a great job of reducing the heap, but ultimately the JMenuItem objs
will exhaust the heap.
If you close the JIFs, most of the JMenuItem-related objs will be released.
Granted, this is not as serious as it could be because most GUIs probably
result in JIFs being closed/disposed. In certain GUI designs, however, some
desktop JIFs may remain on the desktop for the entire session.
Once example is a lightweight/JIF progress monitor that is added to the desktop
and setVisible/toFronted during threaded/long processes. Its JIF is never
closed/disposed & every display (toFront) causes this heap condition. Another
example is our GUI where a row selection in one GUI causes several other JIFs
to be updated.... a JIF setSelected/toFront cascade cannot be used to inform
the users of the JIF updates due to this bug.
Example code (sorry for any cut&paste problems):
/* Demonstrates Selecting JIFs (causing toFront calls) causes lots of JMenuItem
related non-gced objects.
Run with -Xms3M -Xmx6M to reduce heap to show that the objs are not gced &
an outofmem will occur.
Shows apparent JMenuItem & related obj leakage under 1.2.X & 1.3.0 FCS for
L&Fs with System menus
(mostly as long as JIFs are open... many JMenuItem objs are released upon
JIF close). */
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.Timer;
public class JFrame1 extends JFrame {
JDesktopPane desktop;
static int frameCount = 1;
public JFrame1() {
super("JFrame Internal Frames Demo");
try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName
()); }
catch (Exception exc) { System.out.println("Error loading L&F: " +
exc); }
final JSplitPane splitPane = new JSplitPane
(JSplitPane.HORIZONTAL_SPLIT);
final JDesktopPane desktop = new JDesktopPane();
splitPane.setLeftComponent(new JLabel("Test"));
splitPane.setRightComponent(desktop);
final JInternalFrame makeFrame = new JInternalFrame("Frame
Maker",true,false,true,true);
final JButton button = new JButton("Make Frame");
button.addActionListener(new ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent event) {
final JInternalFrame internal =
new JInternalFrame("Frame #"+
(frameCount++),true,true,true,true);
internal.setDefaultCloseOperation
(JInternalFrame.DISPOSE_ON_CLOSE);
internal.getContentPane().add(new JLabel
("Test"),BorderLayout.CENTER);
internal.setBounds( frameCount*10 , frameCount*10 , 200, 150);
internal.setOpaque(true);
internal.setVisible(true);
desktop.add(internal);
internal.toFront();
splitPane.repaint();
}
});
makeFrame.getContentPane().add(button,BorderLayout.CENTER);
// Setup a hasty timer to simulate user JIF selects....
final Timer warningTimer = new Timer(1500, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
final Component[] comps = desktop.getComponents();
SwingUtilities.invokeLater( new Runnable() {
public void run() {
for (int k = 0; k < comps.length; k++) {
// all comps should be JIFs...
try {
JInternalFrame jif = (JInternalFrame)comps[k];
// Do not select JIF with MakeFrame
if (jif != makeFrame) jif.setSelected(true);
}
catch (Exception e) {
System.out.println("setSelected exception
e:"+e);
}
}
}
});
}
});
warningTimer.setCoalesce(true);
warningTimer.setRepeats(true);
final JButton startTimer = new JButton("Start Timer");
final JButton stopTimer = new JButton("Stop Timer");
startTimer.addActionListener(new ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent event) {
if ( desktop.getComponentCount() > 1) {
warningTimer.start();
startTimer.setEnabled(false);
stopTimer.setEnabled(true);
}
else System.out.println("Only 1 JIF exists... Timer thread
NOT spawned");
}
});
makeFrame.getContentPane().add(startTimer,BorderLayout.NORTH);
stopTimer.addActionListener(new ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent event) {
warningTimer.stop();
startTimer.setEnabled(true);
stopTimer.setEnabled(false);
}
});
makeFrame.getContentPane().add(stopTimer,BorderLayout.SOUTH);
makeFrame.setVisible(true);
makeFrame.setBounds(200,0,200,120);
desktop.add(makeFrame);
getContentPane().add(splitPane,BorderLayout.CENTER);
desktop.setSize(500,500);
getContentPane().validate();
getContentPane().setVisible(true);
this.addWindowListener( new WindowAdapter() {
public void windowClosing(WindowEvent event) {
setVisible(false);
dispose();
System.exit(0);
}
});
setSize(750, 560);
show();
}
public static void main(String args[]) {
new JFrame1();
}
}
icsbug
(Review ID: 105277)
======================================================================