java - 关闭 JavaFX 选项卡不会释放该选项卡中 ArrayList 和 TableView 的内存

标签 java memory memory-leaks javafx

我一直试图弄清楚为什么我正在开发的基于 JavaFX 的应用程序会占用如此多的内存。我注意到每次在应用程序中打开新选项卡时内存都会增加,并且在关闭选项卡后不会被垃圾回收。我会不断打开和关闭选项卡,直到最终内存耗尽并崩溃。

所以我编写并尝试了一个非常简单的程序和 GUI。代码如下。我发现,如果我将TableView中的项目设置为null,清除ArrayList,并实例化一个具有相同变量名的新ArrayList,只有这样它才会GC。

这是 Java 和/或 JavaFX 的正常行为吗?正常的是,一旦选项卡关闭,它不会“销毁”选项卡中的那些对象? Java 8/FX 存在错误?

import java.util.ArrayList;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.SplitPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableView;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class TabTest extends Application {

    ArrayList<String> _list;

    @Override
    public void start(Stage primaryStage) {
        SplitPane split = new SplitPane();
        split.setOrientation(Orientation.VERTICAL);     
        HBox window = new HBox();       
        HBox top = new HBox(20);
        HBox bottom = new HBox(20);
        Group group = new Group();
        TabPane tabPane = new TabPane();
        Button btn = new Button("Create Tab");
        top.getChildren().add(btn);
        bottom.getChildren().add(tabPane);
        btn.setOnAction(new EventHandler<ActionEvent>() {            
            @Override
            public void handle(ActionEvent event) {
                createTabAndList(tabPane);
            }
        });
        split.getItems().addAll(top,bottom);
        window.getChildren().add(split);
        group.getChildren().add(window);
        Scene scene = new Scene(group);
        split.prefWidthProperty().bind(scene.widthProperty());
        split.prefHeightProperty().bind(scene.heightProperty());        
        primaryStage.setScene(scene);
        primaryStage.show();        
    }

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

    public void createTabAndList(TabPane tabPane){
        _list = new ArrayList<String>();
        for(int i = 0; i < 10000000; i++){
            _list.add("Test Test Test");
        }
        TableView<String> tb1 = new TableView<String>();
        tb1.setItems(FXCollections.observableArrayList(_list));
        Tab tab = new Tab("Tab1");
        tab.setContent(tb1);
        tabPane.getTabs().add(tab);
        tabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.SELECTED_TAB);        
        tab.setOnClosed(new EventHandler<Event>() {
            @Override
            public void handle(Event arg0) {
                tb1.setItems(null);
                _list.clear();
                _list = new ArrayList<String>();
            }
        });
    }
}

最佳答案

出于好奇,我玩了一下你的示例。我在 Ubuntu 上使用 jdk1.8.60。首先,有一个全局 _list 字段,它自然会永远保留在内存中,除非您专门清理它。为了简化事情,我将 _list 本地化。然后我删除了 onClosed 处理程序以查看会发生什么。所以方法 createTabAndList 是这样的:

public void createTabAndList(TabPane tabPane){
    List<String> _list = new ArrayList<String>(); //_list is local now
    for(int i = 0; i < 10000000; i++){
        _list.add("Test Test Test");
    }
    TableView<String> tb1 = new TableView<String>();
    tb1.setItems(FXCollections.observableArrayList(_list));
    Tab tab = new Tab("Tab1");
    tab.setContent(tb1);
    tabPane.getTabs().add(tab);
    tabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.SELECTED_TAB);        
    //tab.setOnClosed(new EventHandler<Event>() {
    //    @Override
    //    public void handle(Event arg0) {
    //        tb1.setItems(null);
    //        _list.clear();
    //        _list = new ArrayList<String>();
    //    }
    //});
}
  1. 新启动的应用占用 8MB 空间。
  2. 已创建 1 个选项卡 - 63 MB
  3. 已创建 10 个选项卡 - 560 MB - 现在有 10 个列表实例
  4. 所有 10 个标签页均已关闭 - 62 MB

事实上,JavaFX 似乎保留了对最后一个 Activity 选项卡的引用,包括其内容。 hepdump 的引用路径上没有您的代码,它全部位于 JFX 内部。我多次注意到(表格、CSS 处理器),JFX 缓存了很多不再需要的东西。但在一段时间后,或者当内存变得稀缺时,它通常会被清理。当我多次创建并关闭这 10 个选项卡时,它消耗的内存从未超过这 62 MB。

所以,答案是

  • 事实上,JFX 会记住一些额外的东西,但在合理的范围内。重复打开和关闭选项卡不会无限增加内存。
  • 如果您确实需要内存中如此庞大的数据模型(例如您的示例(10M 行)),那么在关闭时清除选项卡是有意义的。

关于java - 关闭 JavaFX 选项卡不会释放该选项卡中 ArrayList 和 TableView 的内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31928294/

相关文章:

设置最大堆大小时,Java Applet 无法正常启动

java - 从 String 类的 .equals() 方法获取 NPE。如何调试这个

memory - 在什么情况下静态分配比动态分配好?

javascript - 从 WEB 拉取 JSON 时出现内存泄漏

C++ 函数初始化和内存泄漏

java - 从监听器返回值

java - 如何绘制位图的三角形部分

debugging - IDA Pro 中的内存映射类似于 OllyDbg

使用 Drop trait 释放 repr(C) 结构的正确习惯用法

java - Java ImageIO.read() 中的内存泄漏