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

Memory leak when selecting a tab which is not contained in TabPane::getTabs()

XMLWordPrintable

    • x86
    • windows_7

      FULL PRODUCT VERSION :
      java version "1.8.0_74"
      Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
      Java HotSpot(TM) Client VM (build 25.74-b02, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows [version 6.1.7601]

      A DESCRIPTION OF THE PROBLEM :
      When selecting a tabulation that DO NOT belong to tabPane.getTabs(), TabPane selection model keep a pointer on this tab and do not release it.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run that attached application with a memory profiler.

      Click on "Select Dummy" button -> 1 sample of MyTab Class in the profiler
      Click on "Close Dummy" button -> still 1 sample of MyTab Class in the profiler, even after triggering the GC.

      The "Dummy tab" will be released only if a new tab is added in TabPane.

      When I look in the code of the select method of TabPane, I understand the result :

      @Override public void select(Tab tab) {
                  final int itemCount = getItemCount();

                  for (int i = 0; i < itemCount; i++) {
                      final Tab value = getModelItem(i);
                      if (value != null && value.equals(tab)) {
                          select(i);
                          return;
                      }
                  }
                  if (tab != null) {
                      setSelectedItem(tab);
                  }
              }

      If the tab doesn't belng to the TabPane, the setSelectedItem method is called and keep a unnecessary reference on this "tab".



      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      /*
       * To change this license header, choose License Headers in Project Properties.
       * To change this template file, choose Tools | Templates
       * and open the template in the editor.
       */
      package tabpanebug;

      import javafx.application.Application;
      import javafx.event.ActionEvent;
      import javafx.scene.Node;
      import javafx.scene.Scene;
      import javafx.scene.control.Button;
      import javafx.scene.control.Label;
      import javafx.scene.control.Tab;
      import javafx.scene.control.TabPane;
      import javafx.scene.control.ToolBar;
      import javafx.scene.layout.BorderPane;
      import javafx.scene.layout.HBox;
      import javafx.scene.layout.VBox;
      import javafx.stage.Stage;

      /**
       *
       * @author Daniel
       */
      public class TabPaneBug extends Application {

          private static class MyTab extends Tab {

              private int i = 0;

              private static int n = 0;
      // private Node content;

              public MyTab() {
                  setContent(mkContent());
      // selectedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean isSelected) -> {
      // if (isSelected) {
      // Node content = mkContent();
      // setContent(content);
      // } else {
      // setContent(null);
      // }
      // });
                  setClosable(true);
                  setText("Tab" + n++);
              }

              private Node mkContent() {
                  VBox vBox = new VBox();
                  for (int j = 0; j < 1000; j++) {
                      HBox hb = new HBox(new Label("bala" + i), new Label("bala" + i), new Label("boo" + i), new Label("boo" + i));
                      vBox.getChildren().add(hb);
                  }
                  i++;
                  return vBox;
              }

          }
          MyTab dummy = new MyTab();

          @Override
          public void start(Stage primaryStage) {
              TabPane tabPane = new TabPane();
      // addNTabs(60, tabPane);
              Button add = new Button("add");
              add.setOnAction((ActionEvent event) -> {
                  add1Tab(tabPane);
              });

              Button closeAll = new Button("closeAll");
              closeAll.setOnAction((ActionEvent event) -> {
                  tabPane.getTabs().clear();
              });
              Button selectDummy = new Button("selectDummy");
              selectDummy.setOnAction((ActionEvent event) -> {
                  tabPane.getSelectionModel().select(dummy);
              });
              Button closeDummy = new Button("closeDummy");
              closeDummy.setOnAction((ActionEvent event) -> {
                  tabPane.getTabs().remove(dummy);
                  dummy = null;
              });


              BorderPane root = new BorderPane();
              root.setTop(new ToolBar(selectDummy, closeDummy, add, closeAll));
              root.setCenter(tabPane);
              Scene scene = new Scene(root, 300, 250);

              primaryStage.setTitle("Tab Pane memory leak!");
              primaryStage.setScene(scene);
              primaryStage.show();
          }

          private void add1Tab(TabPane tabPane) {
              MyTab tab = new MyTab();
              tabPane.getTabs().add(tab);
          }

          /**
           * @param args the command line arguments
           */
          public static void main(String[] args) {
              launch(args);
          }

      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Check if tab belong to tabPane before selecting it

            arapte Ambarish Rapte
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated:
              Resolved: