Skip to content

Commit 79fc0d0

Browse files
committed
8232824: Removing TabPane with strong referenced content causes memory leak from weak one
Reviewed-by: kcr, aghaisas
1 parent aa6f3a4 commit 79fc0d0

File tree

3 files changed

+233
-2
lines changed

3 files changed

+233
-2
lines changed

modules/javafx.graphics/src/main/java/javafx/scene/Parent.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2010, 2020, 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
@@ -263,6 +263,7 @@ void printSeq(String prefix, List<Node> nodes) {
263263
private final List<Node> viewOrderChildren = new ArrayList(1);
264264

265265
void markViewOrderChildrenDirty() {
266+
viewOrderChildren.clear();
266267
NodeHelper.markDirty(this, DirtyBits.PARENT_CHILDREN_VIEW_ORDER);
267268
}
268269

@@ -459,7 +460,7 @@ protected void onChanged(Change<Node> c) {
459460
NodeHelper.markDirty(Parent.this, DirtyBits.NODE_FORCE_SYNC);
460461

461462
if (viewOrderChildrenDirty) {
462-
NodeHelper.markDirty(Parent.this, DirtyBits.PARENT_CHILDREN_VIEW_ORDER);
463+
markViewOrderChildrenDirty();
463464
}
464465
}
465466

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright (c) 2020, 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. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
package test.javafx.scene.control;
27+
28+
import javafx.application.Application;
29+
import javafx.application.Platform;
30+
import javafx.scene.Scene;
31+
import javafx.scene.control.Tab;
32+
import javafx.scene.control.TabPane;
33+
import javafx.scene.control.TextField;
34+
import javafx.scene.layout.StackPane;
35+
import javafx.stage.Stage;
36+
37+
import java.lang.ref.WeakReference;
38+
import java.util.concurrent.CountDownLatch;
39+
import java.util.concurrent.TimeUnit;
40+
41+
import junit.framework.Assert;
42+
import org.junit.AfterClass;
43+
import org.junit.BeforeClass;
44+
import org.junit.Test;
45+
import test.util.Util;
46+
import static org.junit.Assert.assertTrue;
47+
48+
public class TabPaneHeaderLeakTest {
49+
50+
private static CountDownLatch startupLatch;
51+
private static StackPane root;
52+
private static Stage stage;
53+
private static TabPane tabPane;
54+
private static WeakReference<TextField> textFieldWeakRef;
55+
private static WeakReference<Tab> tabWeakRef;
56+
57+
public static class TestApp extends Application {
58+
@Override
59+
public void start(Stage primaryStage) throws Exception {
60+
stage = primaryStage;
61+
TextField tf = new TextField("Weak ref TF");
62+
textFieldWeakRef = new WeakReference<>(tf);
63+
Tab tab = new Tab("Tab", tf);
64+
tabWeakRef = new WeakReference<>(tab);
65+
tabPane = new TabPane(tab, new Tab("Tab1"));
66+
tab = null;
67+
tf = null;
68+
69+
root = new StackPane(tabPane);
70+
Scene scene = new Scene(root);
71+
primaryStage.setScene(scene);
72+
primaryStage.setOnShown(l -> {
73+
Platform.runLater(() -> startupLatch.countDown());
74+
});
75+
primaryStage.show();
76+
}
77+
}
78+
79+
@BeforeClass
80+
public static void initFX() throws Exception {
81+
startupLatch = new CountDownLatch(1);
82+
new Thread(() -> Application.launch(TestApp.class, (String[]) null)).start();
83+
assertTrue("Timeout waiting for FX runtime to start",
84+
startupLatch.await(15, TimeUnit.SECONDS));
85+
}
86+
87+
@Test
88+
public void testTabPaneHeaderLeak() throws Exception {
89+
Util.sleep(100);
90+
Util.runAndWait(() -> {
91+
tabPane.getTabs().clear();
92+
root.getChildren().clear();
93+
});
94+
for (int i = 0; i < 10; i++) {
95+
System.gc();
96+
System.runFinalization();
97+
if (tabWeakRef.get() == null &&
98+
textFieldWeakRef.get() == null) {
99+
break;
100+
}
101+
Util.sleep(500);
102+
}
103+
// Ensure that Tab and TextField are GCed.
104+
Assert.assertNull("Couldn't collect Tab", tabWeakRef.get());
105+
Assert.assertNull("Couldn't collect TextField", textFieldWeakRef.get());
106+
}
107+
108+
@AfterClass
109+
public static void teardownOnce() {
110+
Platform.runLater(() -> {
111+
stage.hide();
112+
Platform.exit();
113+
});
114+
}
115+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright (c) 2020, 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. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
package test.javafx.scene.shape;
27+
28+
import javafx.application.Application;
29+
import javafx.application.Platform;
30+
import javafx.scene.Group;
31+
import javafx.scene.Scene;
32+
import javafx.scene.layout.StackPane;
33+
import javafx.scene.shape.Circle;
34+
import javafx.scene.shape.Rectangle;
35+
import javafx.scene.shape.Shape;
36+
import javafx.stage.Stage;
37+
38+
import java.lang.ref.WeakReference;
39+
import java.util.concurrent.CountDownLatch;
40+
import java.util.concurrent.TimeUnit;
41+
42+
import junit.framework.Assert;
43+
import org.junit.AfterClass;
44+
import org.junit.BeforeClass;
45+
import org.junit.Test;
46+
import test.util.Util;
47+
import static org.junit.Assert.assertTrue;
48+
49+
public class ShapeViewOrderLeakTest {
50+
51+
private static CountDownLatch startupLatch;
52+
private static StackPane root;
53+
private static Stage stage;
54+
private static Group group;
55+
private static WeakReference<Shape> shapeWeakRef;
56+
57+
public static class TestApp extends Application {
58+
@Override
59+
public void start(Stage primaryStage) throws Exception {
60+
stage = primaryStage;
61+
Shape shape1 = new Rectangle();
62+
Shape shape2 = new Circle();
63+
shape1.setViewOrder(1);
64+
shape2.setViewOrder(0);
65+
shapeWeakRef = new WeakReference<>(shape1);
66+
67+
group = new Group(shape1, shape2);
68+
shape1 = null;
69+
shape2 = null;
70+
71+
root = new StackPane(group);
72+
Scene scene = new Scene(root);
73+
primaryStage.setScene(scene);
74+
primaryStage.setOnShown(l -> {
75+
Platform.runLater(() -> startupLatch.countDown());
76+
});
77+
primaryStage.show();
78+
}
79+
}
80+
81+
@BeforeClass
82+
public static void initFX() throws Exception {
83+
startupLatch = new CountDownLatch(1);
84+
new Thread(() -> Application.launch(TestApp.class, (String[]) null)).start();
85+
assertTrue("Timeout waiting for FX runtime to start",
86+
startupLatch.await(15, TimeUnit.SECONDS));
87+
}
88+
89+
@Test
90+
public void testShapeViewOrderLeak() throws Exception {
91+
Util.sleep(100);
92+
Util.runAndWait(() -> {
93+
group.getChildren().clear();
94+
root.getChildren().clear();
95+
});
96+
for (int i = 0; i < 10; i++) {
97+
System.gc();
98+
System.runFinalization();
99+
if (shapeWeakRef.get() == null) {
100+
break;
101+
}
102+
Util.sleep(500);
103+
}
104+
// Ensure that Shape is GCed.
105+
Assert.assertNull("Couldn't collect Shape", shapeWeakRef.get());
106+
}
107+
108+
@AfterClass
109+
public static void teardownOnce() {
110+
Platform.runLater(() -> {
111+
stage.hide();
112+
Platform.exit();
113+
});
114+
}
115+
}

0 commit comments

Comments
 (0)