Skip to content

Commit c235f98

Browse files
author
duke
committedFeb 8, 2024
Automatic merge of jdk:master into master
2 parents becc5d6 + e8ceb71 commit c235f98

File tree

2 files changed

+226
-2
lines changed

2 files changed

+226
-2
lines changed
 

‎src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -3280,7 +3280,10 @@ public Rectangle getNodeDimensions(Object value, int row,
32803280
expanded, treeModel.isLeaf(value), row,
32813281
false);
32823282
if(tree != null) {
3283-
// Only ever removed when UI changes, this is OK!
3283+
// Remove previously added components to prevent leak
3284+
// and add the current component returned by cellrenderer for
3285+
// painting and other measurements
3286+
rendererPane.removeAll();
32843287
rendererPane.add(aComponent);
32853288
aComponent.validate();
32863289
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 6507038
27+
* @key headful
28+
* @summary Verifies memory leak in BasicTreeUI TreeCellRenderer
29+
* @run main TreeCellRendererLeakTest
30+
*/
31+
32+
import java.awt.BorderLayout;
33+
import java.awt.Component;
34+
import java.lang.ref.PhantomReference;
35+
import java.lang.ref.Reference;
36+
import java.lang.ref.ReferenceQueue;
37+
import java.util.ArrayList;
38+
import java.util.List;
39+
import java.util.concurrent.CountDownLatch;
40+
41+
import javax.swing.JFrame;
42+
import javax.swing.JLabel;
43+
import javax.swing.JPanel;
44+
import javax.swing.JScrollPane;
45+
import javax.swing.JTabbedPane;
46+
import javax.swing.JTree;
47+
import javax.swing.SwingUtilities;
48+
import javax.swing.tree.DefaultMutableTreeNode;
49+
import javax.swing.tree.DefaultTreeCellRenderer;
50+
import javax.swing.tree.DefaultTreeModel;
51+
import javax.swing.tree.TreeNode;
52+
53+
public final class TreeCellRendererLeakTest {
54+
55+
private static JFrame frame;
56+
private JPanel jPanel1;
57+
private JPanel jPanel2;
58+
private JScrollPane jScrollPane1;
59+
private JTabbedPane jTabbedPane1;
60+
private JTree jTree1;
61+
private DefaultMutableTreeNode defTreeNode;
62+
private DefaultTreeModel model;
63+
64+
private static final CountDownLatch testDone = new CountDownLatch(1);
65+
66+
// Access to referenceList and referenceQueue is guarded by referenceList
67+
private static final List<Reference<JLabel>> referenceList = new ArrayList<>(50);
68+
private static final ReferenceQueue<JLabel> referenceQueue = new ReferenceQueue<>();
69+
70+
71+
// Custom TreeCellRenderer
72+
public static final class TreeCellRenderer extends DefaultTreeCellRenderer {
73+
74+
public TreeCellRenderer() {}
75+
76+
// Create a new JLabel every time
77+
@Override
78+
public Component getTreeCellRendererComponent(
79+
JTree tree,
80+
Object value,
81+
boolean sel,
82+
boolean expanded,
83+
boolean leaf,
84+
int row,
85+
boolean hasFocus) {
86+
JLabel label = new JLabel();
87+
label.setText("TreeNode: " + value.toString());
88+
if (sel) {
89+
label.setBackground(getBackgroundSelectionColor());
90+
} else {
91+
label.setBackground(getBackgroundNonSelectionColor());
92+
}
93+
94+
synchronized (referenceList) {
95+
referenceList.add(new PhantomReference<>(label, referenceQueue));
96+
}
97+
return label;
98+
}
99+
}
100+
101+
public TreeCellRendererLeakTest() {
102+
initComponents();
103+
jTree1.setCellRenderer(new TreeCellRenderer());
104+
Thread updateThread = new Thread(this::runChanges);
105+
updateThread.setDaemon(true);
106+
updateThread.start();
107+
Thread infoThread = new Thread(this::runInfo);
108+
infoThread.setDaemon(true);
109+
infoThread.start();
110+
}
111+
112+
private void initComponents() {
113+
jTabbedPane1 = new JTabbedPane();
114+
jPanel1 = new JPanel();
115+
jScrollPane1 = new JScrollPane();
116+
jTree1 = new JTree();
117+
jPanel2 = new JPanel();
118+
119+
jPanel1.setLayout(new BorderLayout());
120+
121+
jScrollPane1.setViewportView(jTree1);
122+
123+
jPanel1.add(jScrollPane1, BorderLayout.CENTER);
124+
125+
jTabbedPane1.addTab("tab1", jPanel1);
126+
127+
jPanel2.setLayout(new BorderLayout());
128+
129+
jTabbedPane1.addTab("tab2", jPanel2);
130+
131+
jTabbedPane1.setSelectedIndex(1);
132+
133+
model = (DefaultTreeModel) jTree1.getModel();
134+
TreeNode root = (TreeNode) model.getRoot();
135+
defTreeNode = (DefaultMutableTreeNode) model.getChild(root, 0);
136+
137+
frame = new JFrame();
138+
frame.getContentPane().add(jTabbedPane1, java.awt.BorderLayout.CENTER);
139+
140+
frame.setSize(200, 200);
141+
frame.setLocationRelativeTo(null);
142+
frame.setVisible(true);
143+
}// </editor-fold>
144+
145+
public static void main(String[] args) throws Exception {
146+
try {
147+
SwingUtilities.invokeAndWait(() -> {
148+
new TreeCellRendererLeakTest();
149+
});
150+
testDone.await();
151+
} finally {
152+
SwingUtilities.invokeAndWait(() -> {
153+
if (frame != null) {
154+
frame.dispose();
155+
}
156+
});
157+
}
158+
}
159+
160+
// Periodically cause a nodeChanged() for one of the nodes
161+
public void runChanges() {
162+
long count = 0;
163+
long time = System.currentTimeMillis();
164+
long tm = System.currentTimeMillis();
165+
while ((tm - time) < (15 * 1000)) {
166+
final long currentCount = count;
167+
try {
168+
SwingUtilities.invokeAndWait(() -> {
169+
defTreeNode.setUserObject("runcount " + currentCount);
170+
model.nodeChanged(defTreeNode);
171+
});
172+
count++;
173+
Thread.sleep(1000);
174+
tm = System.currentTimeMillis();
175+
System.out.println("time elapsed " + (tm - time)/1000 + " s");
176+
} catch (InterruptedException ex) {
177+
break;
178+
} catch (Exception e) {
179+
e.printStackTrace();
180+
}
181+
}
182+
testDone.countDown();
183+
}
184+
185+
// Print number of uncollected JLabels
186+
public void runInfo() {
187+
final long time = System.currentTimeMillis();
188+
long removedLabels = 0;
189+
while ((System.currentTimeMillis() - time) < (15 * 1000)) {
190+
System.gc();
191+
192+
int start;
193+
int removed = 0;
194+
int left;
195+
// Remove dead references
196+
synchronized (referenceList) {
197+
start = referenceList.size();
198+
Reference<?> ref;
199+
while ((ref = referenceQueue.poll()) != null) {
200+
referenceList.remove(ref);
201+
removed++;
202+
}
203+
left = referenceList.size();
204+
}
205+
removedLabels += removed;
206+
System.out.println("Live JLabels: " + start + " - " + removed + " = " + left);
207+
System.out.println("All time removed: " + removedLabels);
208+
try {
209+
Thread.sleep(1000);
210+
} catch (InterruptedException ex) {
211+
ex.printStackTrace();
212+
break;
213+
}
214+
}
215+
216+
System.out.println("\nCleaned up labels: " + removedLabels);
217+
if (removedLabels == 0) {
218+
throw new RuntimeException("TreeCellRenderer component leaked");
219+
}
220+
}
221+
}

0 commit comments

Comments
 (0)
Please sign in to comment.