ADDITIONAL SYSTEM INFORMATION :
Windows 10 64-bit
A DESCRIPTION OF THE PROBLEM :
Changing the ListView's data model using setItems prevents ListView from being garbage collected because SelectedItemsReadOnlyObservableList adds a non-weak listener to the data model (ObservableList)
REGRESSION : Last worked in version 8u192
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
- create a ListView
- change its data model by calling setItems( ObservableList<T> )
- clear all references to the ListView but keep the data model
- ListView cannot be garbage collected. This worked in earlier releases (JavaFX 8)
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
In earlier releases the ListView could be garbage collected even when the data model was kept
ACTUAL -
see UnitTest: test succeeded with JavaFX 8, but fails with JavaFX 11.0.2 and 12.0.1
---------- BEGIN SOURCE ----------
package listview;
import static org.junit.Assert.fail;
import java.lang.ref.WeakReference;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.ListView;
import javafx.stage.Stage;
public class ListViewTest {
static CountDownLatch startupLatch;
private WeakReference<ListView<?>> listViewRef;
public static class TestApp extends Application {
@Override
public void start(Stage stage) throws Exception {
startupLatch.countDown();
}
}
private static ObservableList<String> items = FXCollections.observableArrayList();
public ListViewTest() {
}
@BeforeClass
public static void initJavaFX() {
startupLatch = new CountDownLatch(1);
new Thread(() -> Application.launch(TestApp.class, (String[]) null)).start();
try {
if (!startupLatch.await(15, TimeUnit.SECONDS)) {
fail("Timeout waiting for FX runtime to start");
}
} catch (InterruptedException ex) {
fail("Unexpected exception: " + ex);
}
}
@Before
public void initListView() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(() -> {
listViewRef = new WeakReference<>(new ListView<>(items));
latch.countDown();
});
if (!latch.await(15, TimeUnit.SECONDS)) {
fail("Timeout waiting for FX listview initialization");
}
}
@Test
public void testGarbageCollect() {
for (int i = 0; i < 10; i++) {
System.gc();
System.runFinalization();
if (listViewRef.get() == null) {
break;
}
try {
Thread.sleep(500);
} catch (Exception ex) {
}
}
Assert.assertTrue("Could not garbage collect listview", listViewRef.get() == null);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
clear data model by calling setItems(null) to dispose ListView
FREQUENCY : always