-
Bug
-
Resolution: Fixed
-
P3
-
5.0, 6
-
b07
-
generic, x86
-
generic, linux
FULL PRODUCT VERSION :
java version "1.5.0_02"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_02-b09)
Java HotSpot(TM) Client VM (build 1.5.0_02-b09, mixed mode)
FULL OS VERSION :
Linux jw0 2.4.21-1mm.6mdkcustom #2 Wed Sep 10 16:01:39 CEST 2003 i686 unknown unknown GNU/Linux
EXTRA RELEVANT SYSTEM CONFIGURATION :
This bug is os independent.
A DESCRIPTION OF THE PROBLEM :
There is a memory leak caused by method javax.swing.plaf.basic.BasicTreeUI:
The method paint(Graphics,JComponent) is using a field named 'drawingCache',
which is a HashMap to store already painted TreePath instances in.
The paint method clears that cache at the BEGINNING of each paint invocation, instead of clearing the cache at the END of the invocation.
If your JTree is not showing for some time, and you are applying changes to its TreeModel, than the HashMap keeps references to obsolete TreePath instances, and thus references to TreeNode instances which otherwise may would be garbage collected.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Please read the source code of the method.
REPRODUCIBILITY :
This bug can be reproduced always.
CUSTOMER SUBMITTED WORKAROUND :
Subclass BasicTreeUI and overwrite the paint method.
Please note you would have to overwrite the paint method of all BasicTreeUI subclasses, which is impossible, because there are many LookAndFeel implementations, and the user may have selected one outside of the JDK.
Thus the only real fix is to fix BasicTreeUI itself.
public void paint(Graphics g, JComponent c)
{
super.paint(g, c);
super.drawingCache.clear();
}
The other workaround is to ensure your TreeNode instances do not hold references to memory intensive objects then they are not used anymore.
###@###.### 2005-04-19 23:50:23 GMT
Suggested fix from java.net member leouser:
A DESCRIPTION OF THE FIX :
BUG ID: 6258067 memory leak in method javax.swing.plaf.basic.BasicTreeUI.paint
Files: javax.swing.plaf.basic.BasicTreeUI
JDK jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin
This is a simple patch that ensures a cache is cleared before the method returns. It also removes an abundance of compiler warnings that are were being generated for things like not using generics, no serialVersionID, etc...
unified diff:
--- /home/nstuff/java6/jdk1.6.0/javax/swing/plaf/basic/BasicTreeUI.java Thu Dec 15 02:17:46 2005
+++ /home/javarefs/javax/swing/plaf/basic/BasicTreeUI.java Fri Jan 6 20:56:18 2006
@@ -1189,12 +1189,14 @@
hasBeenExpanded = tree.hasBeenExpanded(path);
}
bounds = treeState.getBounds(path, boundsBuffer);
- if(bounds == null)
+ if(bounds == null){
// This will only happen if the model changes out
// from under us (usually in another thread).
// Swing isn't multithreaded, but I'll put this
// check in anyway.
+ drawingCache.clear();
return;
+ }
bounds.x += insets.left;
bounds.y += insets.top;
// See if the vertical line to the parent has been drawn.
@@ -1245,6 +1247,7 @@
// Empty out the renderer pane, allowing renderers to be gc'ed.
rendererPane.removeAll();
+ drawingCache.clear();
}
private boolean isDropLine(JTree.DropLocation loc) {
@@ -2084,9 +2087,10 @@
Object newValue = oldEditor.getCellEditorValue();
Rectangle editingBounds = getPathBounds(tree,
editingPath);
+ KeyboardFocusManager kbfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
boolean requestFocus = (tree != null &&
- (tree.hasFocus() || SwingUtilities.
- findFocusOwner(editingComponent) != null));
+ (tree.hasFocus() || kbfm.
+ getFocusOwner() != null));
editingComponent = null;
editingPath = null;
@@ -2921,6 +2925,7 @@
* incrementing the selection.
*/
public class TreeTraverseAction extends AbstractAction {
+ static final long serialVersionUID = 1084071261129400259L;
/** Determines direction to traverse, 1 means expand, -1 means
* collapse. */
protected int direction;
@@ -2953,6 +2958,7 @@
/** TreePageAction handles page up and page down events.
*/
public class TreePageAction extends AbstractAction {
+ static final long serialVersionUID = 1116016439509828749L;
/** Specifies the direction to adjust the selection by. */
protected int direction;
/** True indicates should set selection from anchor path. */
@@ -2988,6 +2994,7 @@
* is moved up or down based on direction.
*/
public class TreeIncrementAction extends AbstractAction {
+ static final long serialVersionUID = -7928122799257518333L;
/** Specifies the direction to adjust the selection by. */
protected int direction;
/** If true the new item is added to the selection, if false the
@@ -3025,6 +3032,7 @@
* direction.
*/
public class TreeHomeAction extends AbstractAction {
+ static final long serialVersionUID = -6667517417788995069L;
protected int direction;
/** Set to true if append to selection. */
private boolean addToSelection;
@@ -3059,6 +3067,7 @@
* For the first selected row expandedness will be toggled.
*/
public class TreeToggleAction extends AbstractAction {
+ static final long serialVersionUID = -4366649580905350431L;
public TreeToggleAction(String name) {
}
@@ -3078,6 +3087,7 @@
* ActionListener that invokes cancelEditing when action performed.
*/
public class TreeCancelEditingAction extends AbstractAction {
+ static final long serialVersionUID = -5460327479697382196L;
public TreeCancelEditingAction(String name) {
}
@@ -3185,8 +3195,8 @@
private static final TransferHandler defaultTransferHandler = new TreeTransferHandler();
- static class TreeTransferHandler extends TransferHandler implements UIResource, Comparator {
-
+ static class TreeTransferHandler extends TransferHandler implements UIResource, Comparator<TreePath> {
+ static final long serialVersionUID = -7772570847744334546L;
private JTree tree;
/**
@@ -3239,9 +3249,9 @@
return null;
}
- public int compare(Object o1, Object o2) {
- int row1 = tree.getRowForPath((TreePath)o1);
- int row2 = tree.getRowForPath((TreePath)o2);
+ public int compare(TreePath o1, TreePath o2) {
+ int row1 = tree.getRowForPath(o1);
+ int row2 = tree.getRowForPath(o2);
return row1 - row2;
}
@@ -3260,7 +3270,7 @@
*/
TreePath[] getDisplayOrderPaths(TreePath[] paths) {
// sort the paths to display order rather than selection order
- ArrayList selOrder = new ArrayList();
+ ArrayList<TreePath> selOrder = new ArrayList<TreePath>();
for (int i = 0; i < paths.length; i++) {
selOrder.add(paths[i]);
}
@@ -3268,7 +3278,7 @@
int n = selOrder.size();
TreePath[] displayPaths = new TreePath[n];
for (int i = 0; i < n; i++) {
- displayPaths[i] = (TreePath) selOrder.get(i);
+ displayPaths[i] = selOrder.get(i);
}
return displayPaths;
}
JUnit TESTCASE :
Im not sure how to test this minor modification. Really the only thing you can do is watch the memory from one instance to another and maybe see if there is a differance. If someone has a testing idea to implement send this back to me and Ill try to implement it.
FIX FOR BUG NUMBER:
6258067
java version "1.5.0_02"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_02-b09)
Java HotSpot(TM) Client VM (build 1.5.0_02-b09, mixed mode)
FULL OS VERSION :
Linux jw0 2.4.21-1mm.6mdkcustom #2 Wed Sep 10 16:01:39 CEST 2003 i686 unknown unknown GNU/Linux
EXTRA RELEVANT SYSTEM CONFIGURATION :
This bug is os independent.
A DESCRIPTION OF THE PROBLEM :
There is a memory leak caused by method javax.swing.plaf.basic.BasicTreeUI:
The method paint(Graphics,JComponent) is using a field named 'drawingCache',
which is a HashMap to store already painted TreePath instances in.
The paint method clears that cache at the BEGINNING of each paint invocation, instead of clearing the cache at the END of the invocation.
If your JTree is not showing for some time, and you are applying changes to its TreeModel, than the HashMap keeps references to obsolete TreePath instances, and thus references to TreeNode instances which otherwise may would be garbage collected.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Please read the source code of the method.
REPRODUCIBILITY :
This bug can be reproduced always.
CUSTOMER SUBMITTED WORKAROUND :
Subclass BasicTreeUI and overwrite the paint method.
Please note you would have to overwrite the paint method of all BasicTreeUI subclasses, which is impossible, because there are many LookAndFeel implementations, and the user may have selected one outside of the JDK.
Thus the only real fix is to fix BasicTreeUI itself.
public void paint(Graphics g, JComponent c)
{
super.paint(g, c);
super.drawingCache.clear();
}
The other workaround is to ensure your TreeNode instances do not hold references to memory intensive objects then they are not used anymore.
###@###.### 2005-04-19 23:50:23 GMT
Suggested fix from java.net member leouser:
A DESCRIPTION OF THE FIX :
BUG ID: 6258067 memory leak in method javax.swing.plaf.basic.BasicTreeUI.paint
Files: javax.swing.plaf.basic.BasicTreeUI
JDK jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin
This is a simple patch that ensures a cache is cleared before the method returns. It also removes an abundance of compiler warnings that are were being generated for things like not using generics, no serialVersionID, etc...
unified diff:
--- /home/nstuff/java6/jdk1.6.0/javax/swing/plaf/basic/BasicTreeUI.java Thu Dec 15 02:17:46 2005
+++ /home/javarefs/javax/swing/plaf/basic/BasicTreeUI.java Fri Jan 6 20:56:18 2006
@@ -1189,12 +1189,14 @@
hasBeenExpanded = tree.hasBeenExpanded(path);
}
bounds = treeState.getBounds(path, boundsBuffer);
- if(bounds == null)
+ if(bounds == null){
// This will only happen if the model changes out
// from under us (usually in another thread).
// Swing isn't multithreaded, but I'll put this
// check in anyway.
+ drawingCache.clear();
return;
+ }
bounds.x += insets.left;
bounds.y += insets.top;
// See if the vertical line to the parent has been drawn.
@@ -1245,6 +1247,7 @@
// Empty out the renderer pane, allowing renderers to be gc'ed.
rendererPane.removeAll();
+ drawingCache.clear();
}
private boolean isDropLine(JTree.DropLocation loc) {
@@ -2084,9 +2087,10 @@
Object newValue = oldEditor.getCellEditorValue();
Rectangle editingBounds = getPathBounds(tree,
editingPath);
+ KeyboardFocusManager kbfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
boolean requestFocus = (tree != null &&
- (tree.hasFocus() || SwingUtilities.
- findFocusOwner(editingComponent) != null));
+ (tree.hasFocus() || kbfm.
+ getFocusOwner() != null));
editingComponent = null;
editingPath = null;
@@ -2921,6 +2925,7 @@
* incrementing the selection.
*/
public class TreeTraverseAction extends AbstractAction {
+ static final long serialVersionUID = 1084071261129400259L;
/** Determines direction to traverse, 1 means expand, -1 means
* collapse. */
protected int direction;
@@ -2953,6 +2958,7 @@
/** TreePageAction handles page up and page down events.
*/
public class TreePageAction extends AbstractAction {
+ static final long serialVersionUID = 1116016439509828749L;
/** Specifies the direction to adjust the selection by. */
protected int direction;
/** True indicates should set selection from anchor path. */
@@ -2988,6 +2994,7 @@
* is moved up or down based on direction.
*/
public class TreeIncrementAction extends AbstractAction {
+ static final long serialVersionUID = -7928122799257518333L;
/** Specifies the direction to adjust the selection by. */
protected int direction;
/** If true the new item is added to the selection, if false the
@@ -3025,6 +3032,7 @@
* direction.
*/
public class TreeHomeAction extends AbstractAction {
+ static final long serialVersionUID = -6667517417788995069L;
protected int direction;
/** Set to true if append to selection. */
private boolean addToSelection;
@@ -3059,6 +3067,7 @@
* For the first selected row expandedness will be toggled.
*/
public class TreeToggleAction extends AbstractAction {
+ static final long serialVersionUID = -4366649580905350431L;
public TreeToggleAction(String name) {
}
@@ -3078,6 +3087,7 @@
* ActionListener that invokes cancelEditing when action performed.
*/
public class TreeCancelEditingAction extends AbstractAction {
+ static final long serialVersionUID = -5460327479697382196L;
public TreeCancelEditingAction(String name) {
}
@@ -3185,8 +3195,8 @@
private static final TransferHandler defaultTransferHandler = new TreeTransferHandler();
- static class TreeTransferHandler extends TransferHandler implements UIResource, Comparator {
-
+ static class TreeTransferHandler extends TransferHandler implements UIResource, Comparator<TreePath> {
+ static final long serialVersionUID = -7772570847744334546L;
private JTree tree;
/**
@@ -3239,9 +3249,9 @@
return null;
}
- public int compare(Object o1, Object o2) {
- int row1 = tree.getRowForPath((TreePath)o1);
- int row2 = tree.getRowForPath((TreePath)o2);
+ public int compare(TreePath o1, TreePath o2) {
+ int row1 = tree.getRowForPath(o1);
+ int row2 = tree.getRowForPath(o2);
return row1 - row2;
}
@@ -3260,7 +3270,7 @@
*/
TreePath[] getDisplayOrderPaths(TreePath[] paths) {
// sort the paths to display order rather than selection order
- ArrayList selOrder = new ArrayList();
+ ArrayList<TreePath> selOrder = new ArrayList<TreePath>();
for (int i = 0; i < paths.length; i++) {
selOrder.add(paths[i]);
}
@@ -3268,7 +3278,7 @@
int n = selOrder.size();
TreePath[] displayPaths = new TreePath[n];
for (int i = 0; i < n; i++) {
- displayPaths[i] = (TreePath) selOrder.get(i);
+ displayPaths[i] = selOrder.get(i);
}
return displayPaths;
}
JUnit TESTCASE :
Im not sure how to test this minor modification. Really the only thing you can do is watch the memory from one instance to another and maybe see if there is a differance. If someone has a testing idea to implement send this back to me and Ill try to implement it.
FIX FOR BUG NUMBER:
6258067