Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8312963

Memory Leak in TableView if the cell contains a big VBox

XMLWordPrintable

    • x86_64
    • windows_10

      A DESCRIPTION OF THE PROBLEM :
      If you have a TableView and use setGraphic() to add a VBox with some children (the more the better) and then add the vbox x-times and always scroll down to the last element the memory keeps getting up. Even if you trigger a garbage collect, the memory will not be released again.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      I worked out a code example and added it to the report. To reproduce: create a TableView, use setGraphic() in a cell and add a VBox with lots of children there. Then keep adding rows with big VBoxes and scroll down to the last element.


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      I would expect that at some point the memory goes down again
      ACTUAL -
      The memory never goes down, it keeps getting up

      ---------- BEGIN SOURCE ----------

      import java.lang.management.ManagementFactory;
      import java.util.HashMap;
      import java.util.Map;
      import java.util.concurrent.Executors;
      import java.util.concurrent.ScheduledExecutorService;
      import java.util.concurrent.ScheduledFuture;
      import java.util.concurrent.TimeUnit;

      import javafx.application.Application;
      import javafx.application.Platform;
      import javafx.collections.FXCollections;
      import javafx.collections.ObservableList;
      import javafx.scene.Scene;
      import javafx.scene.control.Label;
      import javafx.scene.control.TableCell;
      import javafx.scene.control.TableColumn;
      import javafx.scene.control.TableView;
      import javafx.scene.control.cell.MapValueFactory;
      import javafx.scene.layout.BorderPane;
      import javafx.scene.layout.VBox;
      import javafx.scene.text.Text;
      import javafx.stage.Stage;

      public class Bug extends Application {

      private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);

      public static void main(String[] args) {
      launch(args);
      }

      @SuppressWarnings({ "unchecked", "rawtypes" })
      @Override
      public void start(Stage primaryStage) throws Exception {
      long startTime = System.nanoTime();
      TableView<Map<String, Object>> tableView = new TableView<>();
      ObservableList<Map<String, Object>> data = FXCollections.observableArrayList();
      tableView.setItems(data);

      TableColumn<Map<String, Object>, String> firstCol = new TableColumn<>("first");
      firstCol.setPrefWidth(150);
      TableColumn<Map<String, Object>, String> secondCol = new TableColumn<>("second");
      tableView.getColumns().addAll(firstCol, secondCol);

      firstCol.setCellValueFactory(new MapValueFactory("first"));
      secondCol.setCellFactory(param -> new TableCell<Map<String, Object>, String>() {
      @Override
      public void updateItem(String item, boolean empty) {
      super.updateItem(item, empty);
      VBox box = new VBox();
      box.getChildren().addAll(new Label("We have"),
      new Label(String.valueOf(getTableView().getItems().size())), new Label("items"),
      new Label("in the table"),
      new Label("running time: " + TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime) + " seconds"),
      new Label("memory used: " + ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() + " bytes"),
      new Label("and a lot"),
      new Label("labels"),
      new Text("and"),
      new Text("Text"),
      new Text("and"),
      new Text("such")
      );
      setGraphic(box);
      }
      });

      BorderPane root = new BorderPane();
      root.setCenter(tableView);

      Scene scene = new Scene(root, 300, 400);
      primaryStage.setScene(scene);
      primaryStage.show();

      Runnable beeper = new Runnable() {
      @Override
      public void run() {
      Map<String, Object> map = new HashMap<>();
      map.put("first", "test" + System.currentTimeMillis());
      data.add(map);
      }
      };
      ScheduledFuture<?> beeperHandle = scheduler.scheduleAtFixedRate(beeper, 1, 2, TimeUnit.MILLISECONDS);
      scheduler.schedule(new Runnable() {
      @Override
      public void run() {
      beeperHandle.cancel(true);
      }
      }, 60 * 60L, TimeUnit.SECONDS);

      Runnable beeperRefresh = new Runnable() {
      @Override
      public void run() {
      Platform.runLater(() -> {
      tableView.scrollTo(tableView.getItems().size());
      });
      }
      };
      ScheduledFuture<?> beeperHandler2 = scheduler.scheduleAtFixedRate(beeperRefresh, 1, 5, TimeUnit.SECONDS);
      scheduler.schedule(new Runnable() {
      @Override
      public void run() {
      beeperHandler2.cancel(true);
      }
      }, 60 * 60L, TimeUnit.SECONDS);
      }
      }
      ---------- END SOURCE ----------

        1. Bug_8312963_TTVLeak.java
          4 kB
          Andy Goryachev
        2. Bug.java
          3 kB
          Praveen Narayanaswamy
        3. Capture2.PNG
          94 kB
          Praveen Narayanaswamy
        4. Screenshot 2023-07-26 at 14.32.16.png
          447 kB
          Andy Goryachev

            pnarayanaswa Praveen Narayanaswamy
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: