如何在关闭JavaFX舞台后释放内存?

10
我正在创建一个JavaFx应用程序,当我点击一个按钮时,它将在新的stage中打开一个表格。但我的问题是,当我关闭那个表格的stage时,应用程序没有释放内存。是JavaFX有问题吗?还是我需要做些其他的事情?
我已经尝试在关闭那个stage的同时将所有内容设置为null,但仍然无法释放内存。
表格stage上的关闭事件:
TableView tableView;
Stage myStage;
this.myStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent t) {
                TableController.this.myStage.close();
                tableView.getItems().clear();
                tableView.getColumns().clear();
                tableView = null;
                TableController.this.myStage = null;
                System.gc();
            }
        });

我创建了一个名为replaceScene的方法,用于使用fxml文件加载场景到舞台。它将返回其控制器并将场景设置到舞台中。
 public static Initializable replaceScene(String fXml, Stage mystage) {
    InputStream in = null;
    try {
        FXMLLoader loader = new FXMLLoader();
        in = Utility.class.getResourceAsStream(fXml);
        loader.setLocation(Utility.class.getResource(fXml));
        loader.setBuilderFactory(new JavaFXBuilderFactory());
        AnchorPane page;
        try {
            page = (AnchorPane) loader.load(in);
        } finally {
            in.close();
        }
        Scene scene = new Scene(page);
        mystage.setScene(scene);
        return loader.getController();
    } catch (Exception ex) {
        return null;
    }
}

我首先获取FXML文件的StreamObject(java.io.InputStream),然后将此streamObject传递给FxmlLoader以加载页面。

in = Utility.class.getResourceAsStream(fXml);

我在 in 输入流对象中获取 sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream 对象。
2个回答

8
内存管理和JavaFX没有什么特别之处。如果达到定义的内存限制,GC将运行。将对象设置为null后,GC不会运行。即使调用System.gc()也不意味着GC会运行。来自Javadoc的
调用gc方法“建议”Java虚拟机花费力气回收未使用的对象,以便使它们当前占用的内存可用于快速重用。
要确定是否存在内存泄漏,您可以使用VisualVM,例如查看应用程序是否会耗尽内存(以及原因)。

我的意思是,我使用inputstream加载场景的方式不会释放内存。 - Ronak Jain
1
@Ronak 你为什么这样认为?在你的看法中,哪些对象不会被垃圾回收?你是否通过VisualVM进行了检查? - Kai
1
如果我一遍又一遍地打开和关闭那个表的舞台,那么内存会持续增加。但是在关闭舞台事件时应该减少内存。 - Ronak Jain
1
@Ronak,再次提醒:当某个对象被关闭或设置为null时,虚拟机不会运行垃圾回收(这将导致非常糟糕的性能)。只有当你的应用程序所占用的内存变得很低时,垃圾回收才会启动。尝试学习垃圾回收的工作原理,也许Java中的垃圾回收是如何工作的可以帮助你。 - Kai

2
我即将发布一个非常相似的问题。我必须承认,虽然我不是一名软件工程师,但我的应用程序开发风格可能依赖于一些“非精细”的实践和风格。我喜欢Java内置的内存管理。但我不喜欢Java内置的内存管理。
例如,在Visual Basic 6中,一旦对象的引用计数达到零,可以确保它将被销毁。在Visual Basic 6中实现模态对话框窗口非常简单:
Set myForm = new frmGetClientData
Call myForm.Initialize()
myForm.show, vbModal
nResult = myForm.getResult()
myForm.Hide()
Set myForm = nothing

一旦对myForm的引用被清除,它将被销毁。这很方便,因为您可以确保每次都会构建和初始化一个新表单。
在Java中,处理模态对话框非常困难。除非传递引用,否则对话框控制器对象无法了解父控制器对象的任何信息。同样,如果需要调用其方法,则父控制器对象必须获取对话框控制器的引用。当构建新阶段时,父控制器必须获取对其自己阶段的引用,以将对话框阶段的模式设置为模态。
由于所有这些引用相互指向,因此在Java中,模态对话框窗口似乎永远不会被垃圾回收(因为指向父窗口和控制器的引用应该保持有效),并且每次使用FXMLLoader来构建和显示对话框窗口时,都会在内存中产生一个具有很长寿命的新大型对象。在Java中使用模态对话框会导致内存泄漏。
一种解决方案是进入Visual Basic模式,并在不再使用时勤于释放对话框窗口和控制器的引用。这很麻烦。这仍然不能保证窗口何时被销毁。
另一种解决方案是创建对话框池,并使用初始化方法构建它们,每次需要它们时将它们设置为已知的初始状态,而不是每次想显示对话框窗口时都构建新窗口(这就像我在Visual Basic中所做的那样)。
有人对JavaFX的此情况有了解吗?我正在编写一个辅助类来帮助我构建和管理模态对话框窗口,但对我来说这似乎不应该是必需的。也许我只是给自己增加了额外的工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接