Skip to content

Commit 91d4c8b

Browse files
committed
8241737: TabPaneSkin memory leak on replacing selectionModel
Reviewed-by: fastegal, kcr
1 parent 48476eb commit 91d4c8b

File tree

3 files changed

+70
-15
lines changed

3 files changed

+70
-15
lines changed

modules/javafx.controls/src/main/java/javafx/scene/control/skin/TabPaneSkin.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
import javafx.scene.control.Label;
7070
import javafx.scene.control.MenuItem;
7171
import javafx.scene.control.RadioMenuItem;
72+
import javafx.scene.control.SelectionModel;
7273
import javafx.scene.control.SkinBase;
7374
import javafx.scene.control.Tab;
7475
import javafx.scene.control.TabPane;
@@ -195,12 +196,9 @@ public TabPaneSkin(TabPane control) {
195196
}
196197

197198
initializeTabListener();
199+
updateSelectionModel();
198200

199-
registerChangeListener(control.getSelectionModel().selectedItemProperty(), e -> {
200-
isSelectingTab = true;
201-
selectedTab = getSkinnable().getSelectionModel().getSelectedItem();
202-
getSkinnable().requestLayout();
203-
});
201+
registerChangeListener(control.selectionModelProperty(), e -> updateSelectionModel());
204202
registerChangeListener(control.sideProperty(), e -> updateTabPosition());
205203
registerChangeListener(control.widthProperty(), e -> clipRect.setWidth(getSkinnable().getWidth()));
206204
registerChangeListener(control.heightProperty(), e -> clipRect.setHeight(getSkinnable().getHeight()));
@@ -257,8 +255,6 @@ public TabPaneSkin(TabPane control) {
257255
}
258256
};
259257

260-
261-
262258
/***************************************************************************
263259
* *
264260
* Public API *
@@ -267,6 +263,11 @@ public TabPaneSkin(TabPane control) {
267263

268264
/** {@inheritDoc} */
269265
@Override public void dispose() {
266+
if (selectionModel != null) {
267+
selectionModel.selectedItemProperty().removeListener(weakSelectionChangeListener);
268+
selectionModel = null;
269+
}
270+
270271
super.dispose();
271272

272273
if (behavior != null) {
@@ -429,6 +430,25 @@ public TabPaneSkin(TabPane control) {
429430
* *
430431
**************************************************************************/
431432

433+
private SelectionModel<Tab> selectionModel;
434+
private InvalidationListener selectionChangeListener = observable -> {
435+
isSelectingTab = true;
436+
selectedTab = getSkinnable().getSelectionModel().getSelectedItem();
437+
getSkinnable().requestLayout();
438+
};
439+
private WeakInvalidationListener weakSelectionChangeListener =
440+
new WeakInvalidationListener(selectionChangeListener);
441+
442+
private void updateSelectionModel() {
443+
if (selectionModel != null) {
444+
selectionModel.selectedItemProperty().removeListener(weakSelectionChangeListener);
445+
}
446+
selectionModel = getSkinnable().getSelectionModel();
447+
if (selectionModel != null) {
448+
selectionModel.selectedItemProperty().addListener(weakSelectionChangeListener);
449+
}
450+
}
451+
432452
private static int getRotation(Side pos) {
433453
switch (pos) {
434454
case TOP:

modules/javafx.controls/src/test/java/test/javafx/scene/control/SelectionFocusModelMemoryTest.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,6 @@ public void testListViewSelectionModel() {
194194

195195
@Test
196196
public void testTabPaneSelectionModel() {
197-
// FIXME
198-
// can't formally ignore just one parameter, so backing out if showBeforeReplaceSM
199-
if (showBeforeReplaceSM) return; //@Ignore("8241737")
200197
TabPane control = new TabPane();
201198
ObservableList<String> data = FXCollections.observableArrayList("Apple", "Orange", "Banana");
202199
data.forEach(text -> control.getTabs().add(new Tab(text)));

modules/javafx.controls/src/test/java/test/javafx/scene/control/TabPaneTest.java

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
import static org.junit.Assert.assertTrue;
3838
import static org.junit.Assert.fail;
3939

40+
import javafx.scene.control.SelectionModel;
41+
import javafx.scene.control.Skin;
4042
import test.com.sun.javafx.scene.control.infrastructure.StageLoader;
4143
import javafx.application.Platform;
4244
import javafx.beans.property.BooleanProperty;
@@ -1155,27 +1157,26 @@ public void test_rt_38382(boolean addToTabPane) {
11551157
}
11561158

11571159
// Test for JDK-8154039
1158-
WeakReference<Tab> weakTab;
11591160
@Test public void testSelectNonChildTab() {
11601161
tabPane.getTabs().addAll(tab1);
11611162
root.getChildren().add(tabPane);
11621163
show();
11631164
tk.firePulse();
1164-
weakTab = new WeakReference<>(new Tab("NonChildTab"));
1165+
WeakReference<Tab> weakTab = new WeakReference<>(new Tab("NonChildTab"));
11651166
tabPane.getSelectionModel().select(weakTab.get());
11661167
tk.firePulse();
1167-
attemptGC(10);
1168+
attemptGC(10, weakTab);
11681169
tk.firePulse();
11691170
assertNull(weakTab.get());
11701171
}
11711172

1172-
private void attemptGC(int n) {
1173+
private void attemptGC(int n, WeakReference<?> weakRef) {
11731174
// Attempt gc n times
11741175
for (int i = 0; i < n; i++) {
11751176
System.gc();
11761177
System.runFinalization();
11771178

1178-
if (weakTab.get() == null) {
1179+
if (weakRef.get() == null) {
11791180
break;
11801181
}
11811182
try {
@@ -1207,4 +1208,41 @@ private void attemptGC(int n) {
12071208
private int sortCompare(Tab t1, Tab t2) {
12081209
return t2.getText().compareTo(t1.getText());
12091210
}
1211+
1212+
class TabPaneSkin1 extends TabPaneSkin {
1213+
TabPaneSkin1(TabPane tabPane) {
1214+
super(tabPane);
1215+
}
1216+
}
1217+
1218+
@Ignore("JDK-8242621")
1219+
@Test
1220+
public void testNPEOnSwitchSkinAndChangeSelection() {
1221+
// Because of JDK-8242621, this test fails with NPE.
1222+
tabPane.getTabs().addAll(tab1, tab2);
1223+
root.getChildren().add(tabPane);
1224+
stage.show();
1225+
tk.firePulse();
1226+
1227+
tabPane.setSkin(new TabPaneSkin1(tabPane));
1228+
tk.firePulse();
1229+
tabPane.getSelectionModel().select(1);
1230+
tk.firePulse();
1231+
}
1232+
1233+
@Test
1234+
public void testSMLeakOnSwitchSkinAndSM() {
1235+
tabPane.getTabs().addAll(tab1, tab2);
1236+
root.getChildren().add(tabPane);
1237+
stage.show();
1238+
tk.firePulse();
1239+
1240+
WeakReference<SelectionModel<Tab>> weakSMRef = new WeakReference<>(tabPane.getSelectionModel());
1241+
tabPane.setSkin(new TabPaneSkin1(tabPane));
1242+
tk.firePulse();
1243+
tabPane.setSelectionModel(TabPaneShim.getTabPaneSelectionModel(tabPane));
1244+
tk.firePulse();
1245+
attemptGC(10, weakSMRef);
1246+
assertNull(weakSMRef.get());
1247+
}
12101248
}

0 commit comments

Comments
 (0)