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);
    }
}