gpt4 book ai didi

java - 内容更改时 SwingNode 的内容未被垃圾收集

转载 作者:行者123 更新时间:2023-12-05 02:01:49 27 4
gpt4 key购买 nike

我有一个 JavaFX 应用程序,它通过 SwingNode 显示 Swing 图。由于 swing 组件的编写方式以及我不愿意重构它,每次用户需要更新数据时,我都会创建一个新的 swing plot 实例。换句话说,每当用户重新生成绘图时,它都会创建一个新的 Swing 组件并将 SwingNode 的内容设置为新组件。

一切正常,只是我发现 swing 组件永远不会被垃圾回收。它们包含大量数据,因此一段时间后会变成非常严重的内存泄漏。

我已经能够用这个最小的可重现示例来证明这个问题:

public class LeakDemo extends Application {

//Keep week references to all panels that we've ever generated to see if any
//of them get collected.
private Collection<WeakReference<JPanel>> panels = new CopyOnWriteArrayList<>();

@Override
public void start(Stage primaryStage) throws Exception {

SwingNode node = new SwingNode();

Pane root = new Pane();
root.getChildren().add(node);

//Kick off a thread that repeatedly creates new JPanels and resets the swing node's content
new Thread(() -> {

while(true) {

//Lets throw in a little sleep so we can read the output
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}

SwingUtilities.invokeLater(() -> {
JPanel panel = new JPanel();
panels.add(new WeakReference<>(panel));
node.setContent(panel);
});

System.out.println("Panels in memory: " + panels.stream().filter(ref -> ref.get() != null).count());

//I know this doesn't guarantee anything, but prompting a GC gives me more confidence that this
//truly is a bug.
System.gc();
}

}).start();

primaryStage.setScene(new Scene(root, 100, 100));

primaryStage.show();

}

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

程序的输出是

Panels in memory: 0
Panels in memory: 1
Panels in memory: 2
Panels in memory: 3
Panels in memory: 4
Panels in memory: 5
Panels in memory: 6
Panels in memory: 7
Panels in memory: 8
Panels in memory: 9
Panels in memory: 10

并且会继续这样发展到成千上万。

我尝试检查来自 jvisualvm 的堆转储,但在引用文献的海洋中迷失了方向。

我怀疑这是 JavaFX 中的一个问题,但我想在将其报告为错误之前先检查一下这里。

最佳答案

好的,我明白了。

简答

只需将 swing 内容包装在 JPanel(或其他一些 JComponent)中。然后只调用一次 SwingNode.setContent() 来添加包装器。当您需要更新 swing 内容时,调用包装器上的 removeAll(),然后使用适当的内容调用 add()

长答案

感谢此答案中的建议:https://stackoverflow.com/a/66283491/2423283我能够确定泄漏是由 GlassStage 引起的,它是一个非 API 类,除其他外,它保留了 GlassStage 所有实现的静态列表。 SwingNode 的内容由 EmbeddedScene 的实例管理,该实例是 GlassStage 的子类型。

当调用 close() 时,项目将从静态列表中删除。 SwingNode.setContent() 不会关闭任何预先存在的内容,但 Container.removeAll() 会。

工作代码

这是固定代码的示例:

public class LeakDemoFixed extends Application {

//Keep week references to all panels that we've ever generated to see if any
//of them get collected.
private Collection<WeakReference<JPanel>> panels = new CopyOnWriteArrayList<>();

@Override
public void start(Stage primaryStage) throws Exception {

SwingNode node = new SwingNode();

//These 2 lines were added
JComponent swingContent = new JPanel();
node.setContent(swingContent);

Pane root = new Pane();
root.getChildren().add(node);

//Kick off a thread that repeatedly creates new JPanels and resets the swing node's content
new Thread(() -> {

while(true) {

//Lets throw in a little sleep so we can read the output
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}

SwingUtilities.invokeLater(() -> {
JPanel panel = new JPanel();
panels.add(new WeakReference<>(panel));
//Removed the line below
//node.setContent(panel);

//Added these 2 lines
swingContent.removeAll();
swingContent.add(panel);
});

System.out.println("Panels in memory: " + panels.stream().filter(ref -> ref.get() != null).count());

//I know this doesn't guarantee anything, but prompting a GC gives me more confidence that this
//truly is a bug.
System.gc();
}

}).start();

primaryStage.setScene(new Scene(root, 100, 100));

primaryStage.show();

}

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

关于java - 内容更改时 SwingNode 的内容未被垃圾收集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66270491/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com