绘制图表时JavaFX WeakReference内存泄漏

问题描述 投票:2回答:2

我遇到了内存泄漏我无法在我的代码中修复。场景:用户打开一个新窗口,其中绘制了一个图表(在本例中为LineChart)。但是当窗口关闭时,java.lang.ref.WeakReference和javafx.beans.property.BooleanPropertyBase $ Listener保留在内存中。它们的数字完全对应于绘制的数据点(XYChart.Data)。我只是无法弄清楚如何摆脱它们。在我的代码中,许多窗口经常打开和关闭,每个图表有10k-100k数据点,内存填满很快。

我确定我犯了一些愚蠢的错误,但我找不到它。非常感谢帮助!

示例代码:

import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class Test extends Application {

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

    @Override
    public void start(final Stage primaryStage) {
        primaryStage.setTitle("Hello World");
        Group root = new Group();
        Scene scene = new Scene(root, 300, 250, Color.LIGHTGREEN);
        Button btn = new Button();
        btn.setLayoutX(100);
        btn.setLayoutY(80);
        btn.setText("Create stage");
        btn.setOnAction(new EventHandler<ActionEvent>() {

            public void handle(ActionEvent event) {
                new CreateStage();
                primaryStage.toFront();

            }
        });
        root.getChildren().add(btn);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

class CreateStage {

    public CreateStage() {
        Stage stage = new Stage();
            stage.setTitle("Line Chart Sample");
        //Basic Chart attributes
        NumberAxis xAxis = new NumberAxis();
        NumberAxis yAxis = new NumberAxis();
        xAxis.setLabel("RT [minutes]");
        yAxis.setLabel("Intensity");
        LineChart<Number, Number> linechart = new LineChart(xAxis, yAxis);

                XYChart.Series newSeries = new XYChart.Series();

                List<XYChart.Data> list = new ArrayList<>();
                //just fill the chart with data points
                for (int j = 0; j < 10000; j++) {
                    float intensity = j;
                    float currentRT = j;

                    list.add(new XYChart.Data(currentRT, intensity));
                }
                newSeries.getData().addAll(list);

                // add new Series
                linechart.getData().add(newSeries);

        Scene scene  = new Scene(linechart,800,600);       


        stage.setScene(scene);
        stage.show();
    }
}  
java javafx memory-leaks javafx-8
2个回答
1
投票

我能够解决问题。调用

series.getData().clear();

关闭窗口时,每个XYChart.Series都可以摆脱内存泄漏。像javafx.scene.layout.CornerRadii,com.sun.javafx.sg.prism.NGRegion和javafx.scene.layout.Background这样的东西仍然留在内存中,但最终会收集垃圾并且不会累积。

这是固定代码:

import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.ScatterChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class Test extends Application {

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

    @Override
    public void start(final Stage primaryStage) {
        primaryStage.setTitle("Hello World");
        Group root = new Group();
        Scene scene = new Scene(root, 300, 250, Color.LIGHTGREEN);
        Button btn = new Button();
        btn.setLayoutX(100);
        btn.setLayoutY(80);
        btn.setText("Create stage");
        btn.setOnAction(new EventHandler<ActionEvent>() {

            public void handle(ActionEvent event) {
                CreateStage();
                primaryStage.toFront();

            }
        });
        root.getChildren().add(btn);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public void CreateStage() {

        Stage stage = new Stage();
        stage.setTitle("Line Chart Sample");
        //Basic Chart attributes
        NumberAxis xAxis = new NumberAxis();
        NumberAxis yAxis = new NumberAxis();
        xAxis.setLabel("RT [minutes]");
        yAxis.setLabel("Intensity");

        //linechart.getData().clear();
        LineChart<Number, Number> linechart = new LineChart(xAxis, yAxis);

        XYChart.Series newSeries = new XYChart.Series();

        List<XYChart.Data> list = new ArrayList<>();
        //just fill the chart with data points
        for (int j = 0; j < 10000; j++) {
            float intensity = j;
            float currentRT = j;

            list.add(new XYChart.Data(currentRT, intensity));
        }
        newSeries.getData().addAll(list);

        // add new Series
        linechart.getData().add(newSeries);

        Scene scene = new Scene(linechart, 800, 600);

        stage.setScene(scene);
        stage.show();

        //this fixes it
        stage.setOnCloseRequest(event -> {
            for (XYChart.Series series : linechart.getData()) {
                series.getData().clear();
            }

        });

    }
}

感谢@fabian指出我正确的方向。


0
投票

这很可能不是你身边的错误。我遇到了类似的问题,在运行时关闭UI的某些部分之后,一些UI数据仍留在系统内存中。

这可能不是解决问题的最佳方法,但您可以尝试重用Windows。 您可以通过在单独的ArrayLists中跟踪所有打开的窗口和关闭的窗口来完成此操作。

如果用户想要关闭一个窗口,则将窗口设置为不可见(而不是完全关闭它)并将其移动到关闭窗口的ArrayList中。创建新窗口后,您可以检查已关闭窗口的ArrayList(如果包含窗口)。如果是这样,您可以重复使用此窗口(更改此窗口的图形并将窗口设置为可见)并将其从关闭窗口的ArrayList移动​​到打开窗口的ArrayList。

如果要打开一个新窗口并且打开窗口的ArrayList中没有窗口,只需创建一个窗口类的新实例并将其添加到打开窗口的ArrayList中。 这可能不是解决问题的最佳方法,但这就是我解决问题的方法,这与你的问题类似。

© www.soinside.com 2019 - 2024. All rights reserved.