In one of my projects I was fighting with a memory leak the last couple of days (yes … “couple”) and I came to the conclusion that there might be an issue related to touch / scroll gestures. In the sample below I have two buttons. The first one creates a list view with one thousand rows, the second one removes it.

Bildschirmfoto 2016-01-29 um 18.02.58

I made the following observations:

  1. when I click on “create” and immediately on “destroy” then everything will be garbage collected.
  2. when I click on “create” and use the scrollbar to scroll down and then click on “destroy” everything will be garbage collected.
  3. when I click on “create” and then use a gesture to scroll down (with my Mac Magic Mouse) then the garbage collection fails.

I have used jvisualvm that ships with the JDK and I use the “Sampler” tab to look at the heap space. I filter for the “TestItem” class and I can see that always those items are still in memory that were created for the last ListView after pressing the “create” button.

Bildschirmfoto 2016-01-29 um 17.34.16

When I dump the heap and analyze it with the “Eclipse Memory Analyzer” I can see that it is most likely the ScrollGesture that keeps a reference to the list view / the data.

Bildschirmfoto 2016-01-29 um 17.36.02

Can anyone confirm this? Is this a known bug? I could not find anything related to this issue in the Java bug database.

import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class MemoryLeakListViewApp extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
BorderPane pane = new BorderPane();
Scene scene = new Scene(pane);
Button createButton = new Button("Create");
createButton.setOnAction(evt -> {
ListView listView = new ListView();
List children = new ArrayList();
for (int i = 0; i < 1000; i++) {
children.add(new TestItem("Row " + i));
}
listView.getItems().setAll(children);
pane.setCenter(listView);         
});
Button deleteButton = new Button("Destroy");         
deleteButton.setOnAction(evt -> {
pane.setCenter(null);
});
HBox box = new HBox();
box.getChildren().addAll(createButton, deleteButton);
pane.setTop(box);
primaryStage.setScene(scene);
primaryStage.setWidth(800);
primaryStage.setHeight(800);
primaryStage.show();
}
static class TestItem {
private String name;
public TestItem(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
public static void main(String[] args) {
launch(args);
}
}