关闭 JavaFX 选项卡不会从该选项卡中的 ArrayLists 和 TableViews 释放内存
Closing JavaFX tabs doesn't release memory from ArrayLists and TableViews in that tab
我一直在努力弄清楚为什么我正在开发的 Java基于 FX 的应用程序会占用如此多的内存。我注意到每次我在应用程序中打开一个新选项卡时内存都会增加,并且在我关闭选项卡后不会被 GC。我会一直打开和关闭选项卡,直到最终 运行 内存不足并崩溃。
所以我编写并尝试了一个非常简单的程序和 GUI。代码如下。我发现,如果我将TableView中的项目设置为null,清除ArrayList,并实例化一个具有相同变量名的新ArrayList,只有这样它才会被GC。
这是 Java and/or JavaFX 的正常行为吗?正常情况是,一旦选项卡关闭,它就不会 "destroy" 选项卡中的那些对象? 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>();
// }
//});
}
- 新启动的应用占用8MB。
- 创建了 1 个标签 - 63 MB
- 创建了 10 个选项卡 - 560 MB - 现在有 10 个列表实例
- 所有 10 个选项卡已关闭 - 62 MB
事实上,JavaFX 似乎保留了对最后一个活动选项卡的引用,包括其内容。在 hepdump 的参考路径上没有您的代码,它都在 JFX 内部。我多次注意到(表格,css 处理器),JFX 缓存了很多不再需要的东西。但它通常会在一段时间后或内存不足时被清理掉。当我再次创建和关闭这 10 个选项卡时,它从未消耗超过这 62 MB。
所以,答案是
- 的确,JFX 记得一些额外的东西,但在合理的范围内。内存不会因为反复打开和关闭选项卡而无限增长。
- 如果您真的需要内存中如此庞大的数据模型,就像您的示例(1000 万行),那么在关闭时清除选项卡是有意义的。
我一直在努力弄清楚为什么我正在开发的 Java基于 FX 的应用程序会占用如此多的内存。我注意到每次我在应用程序中打开一个新选项卡时内存都会增加,并且在我关闭选项卡后不会被 GC。我会一直打开和关闭选项卡,直到最终 运行 内存不足并崩溃。
所以我编写并尝试了一个非常简单的程序和 GUI。代码如下。我发现,如果我将TableView中的项目设置为null,清除ArrayList,并实例化一个具有相同变量名的新ArrayList,只有这样它才会被GC。
这是 Java and/or JavaFX 的正常行为吗?正常情况是,一旦选项卡关闭,它就不会 "destroy" 选项卡中的那些对象? 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>();
// }
//});
}
- 新启动的应用占用8MB。
- 创建了 1 个标签 - 63 MB
- 创建了 10 个选项卡 - 560 MB - 现在有 10 个列表实例
- 所有 10 个选项卡已关闭 - 62 MB
事实上,JavaFX 似乎保留了对最后一个活动选项卡的引用,包括其内容。在 hepdump 的参考路径上没有您的代码,它都在 JFX 内部。我多次注意到(表格,css 处理器),JFX 缓存了很多不再需要的东西。但它通常会在一段时间后或内存不足时被清理掉。当我再次创建和关闭这 10 个选项卡时,它从未消耗超过这 62 MB。
所以,答案是
- 的确,JFX 记得一些额外的东西,但在合理的范围内。内存不会因为反复打开和关闭选项卡而无限增长。
- 如果您真的需要内存中如此庞大的数据模型,就像您的示例(1000 万行),那么在关闭时清除选项卡是有意义的。