JavaFX-Dispose窗口

JavaFX-Dispose窗口,java,javafx,dispose,Java,Javafx,Dispose,我在JavaFX中有一个窗口需要“清理”,即:重置所有字段的值,并将其设置回初始化时的状态。我想我可以做一些类似于window.dispose()的事情,但结果表明,似乎根本不存在类似的事情 这似乎是窗口引擎中的一个重大疏忽,因为我将要打开多个我不想持久的窗口。如果我执行stage.close()操作,它只会隐藏窗口而不释放内存,这可能会导致内存泄漏。当您在窗口上调用close(),(或者,等效地,hide())时,FX工具包将释放它对该窗口的所有引用。因此,只要不保留对窗口的引用,一旦调用了c

我在JavaFX中有一个窗口需要“清理”,即:重置所有字段的值,并将其设置回初始化时的状态。我想我可以做一些类似于
window.dispose()
的事情,但结果表明,似乎根本不存在类似的事情

这似乎是窗口引擎中的一个重大疏忽,因为我将要打开多个我不想持久的窗口。如果我执行
stage.close()
操作,它只会隐藏窗口而不释放内存,这可能会导致内存泄漏。

当您在窗口上调用
close()
,(或者,等效地,
hide()
)时,FX工具包将释放它对该窗口的所有引用。因此,只要不保留对窗口的引用,一旦调用了
close()
,它就可以进行垃圾收集。在垃圾收集方面,它的行为与任何其他Java对象类似——一旦垃圾被收集,与窗口相关的资源就会被释放

这里有一个演示,它每秒钟打开一个新窗口,关闭前一个窗口。该窗口在标签中显示图像,因此它消耗了合理的内存块。主阶段显示内存统计信息(同时每秒更新一次)。这与您预期的完全一样,内存逐渐增加,直到垃圾回收器启动,此时内存使用量下降。(在我的系统上,它在~7MB到~65MB之间循环;您的里程可能因操作系统、JDK版本和系统资源而异。)您可以插入对
system.gc()的调用(仅用于演示目的;我不建议在实际代码中这样做)

import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.binding.NumberBinding;
import javafx.beans.property.LongProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.util.Duration;

public class ConstantlyOpenWindows extends Application {

    @Override
    public void start(Stage primaryStage) {

        ScheduledService<?> service = periodicallyShowNewWindow();

        Pane root = createMemoryMonitor();
        primaryStage.setScene(new Scene(root, 800, 600));

        primaryStage.show();

        service.start();

    }


    private ScheduledService<Integer> periodicallyShowNewWindow() {

        Screen screen = Screen.getPrimary();
        double maxX = screen.getBounds().getMaxX();

        AtomicInteger count = new AtomicInteger();
        ObjectProperty<Stage> visibleStage = new SimpleObjectProperty<>();
        ScheduledService<Integer> service = new ScheduledService<Integer>() {
            @Override
            protected Task<Integer> createTask() {
                Task<Integer> task = new Task<Integer>() {
                    @Override 
                    public Integer call() {
                        return count.incrementAndGet();
                    }
                };
                return task ;
            }
        };

        service.setOnSucceeded(event -> {
            Stage lastStage = visibleStage.get();
            Stage stage = createWindowWithImage(service.getValue());
            visibleStage.set(stage);
            stage.setX(maxX - 480);
            stage.show();
            if (lastStage != null) {
                lastStage.close();
            }
//            System.gc();
        });

        service.setPeriod(Duration.seconds(1));
        return service;
    }


    private Stage createWindowWithImage(int count) {
        Stage stage = new Stage();
        ImageView image = createImage();
        Label label = new Label("Window "+count);
        label.setGraphic(image);
        label.setContentDisplay(ContentDisplay.BOTTOM);
        stage.setScene( new Scene(new StackPane(label), 480, 500) );
        return stage;
    }

    private ImageView createImage() {
        WritableImage img = new WritableImage(400, 400);
        Random rng = new Random();
        int x = rng.nextInt(40);
        int y = rng.nextInt(40);
        PixelWriter pw = img.getPixelWriter();
        for (int i = 0; i < 400; i++) {
            for (int j = 0 ; j < 400 ; j++) {
                if (i >= x*10 && i < (x+1)*10 && j >= y*10 && j < (y+1) * 10) {
                    pw.setColor(i, j, Color.CORNFLOWERBLUE);
                } else {
                    pw.setColor(i, j, Color.ANTIQUEWHITE);
                }
            }
        }
        return new ImageView(img);
    }

    private Pane createMemoryMonitor() {
        LongProperty totalMemory = new SimpleLongProperty(Runtime.getRuntime().totalMemory());
        LongProperty freeMemory = new SimpleLongProperty(Runtime.getRuntime().freeMemory());
        LongProperty maxMemory = new SimpleLongProperty(Runtime.getRuntime().maxMemory());
        NumberBinding usedMemory = totalMemory.subtract(freeMemory);

        Label usedMemoryLabel = new Label();
        usedMemoryLabel.textProperty().bind(usedMemory.asString("Used memory: %,d"));
        Label freeMemoryLabel = new Label();
        freeMemoryLabel.textProperty().bind(freeMemory.asString("Free memory: %,d"));
        Label totalMemoryLabel = new Label();
        totalMemoryLabel.textProperty().bind(totalMemory.asString("Total memory: %,d"));
        Label maxMemoryLabel = new Label();
        maxMemoryLabel.textProperty().bind(maxMemory.asString("Max memory: %,d"));

        Series<Number, Number> series = new Series<>();
        series.setName("Used memory");

        AtomicInteger time = new AtomicInteger();

        Timeline updateMemory = new Timeline(new KeyFrame(Duration.seconds(1), event -> {
            totalMemory.set(Runtime.getRuntime().totalMemory());
            freeMemory.set(Runtime.getRuntime().freeMemory());
            maxMemory.set(Runtime.getRuntime().maxMemory());
            series.getData().add(new Data<>(time.incrementAndGet(), usedMemory.getValue()));
            if (series.getData().size() > 100) {
                series.getData().subList(0, series.getData().size() - 100).clear();
            }    
        }));
        updateMemory.setCycleCount(Animation.INDEFINITE);
        updateMemory.play();

        VBox labels = new VBox(usedMemoryLabel, freeMemoryLabel, totalMemoryLabel, maxMemoryLabel);
        labels.setAlignment(Pos.CENTER);

        NumberAxis xAxis = new NumberAxis();
        xAxis.setLabel("Time");
        xAxis.setForceZeroInRange(false);
        NumberAxis yAxis = new NumberAxis();
        yAxis.setLabel("Memory");
        LineChart<Number, Number> chart = new LineChart<Number, Number>(xAxis, yAxis);
        chart.setAnimated(false);
        chart.getData().add(series);

        BorderPane root = new BorderPane(chart, labels, null, null, null);

        return root;
    }

    public static void main(String[] args) {
        launch(args);
    }
}
import java.util.Random;
导入java.util.concurrent.AtomicInteger;
导入javafx.animation.animation;
导入javafx.animation.KeyFrame;
导入javafx.animation.Timeline;
导入javafx.application.application;
导入javafx.beans.binding.NumberBinding;
导入javafx.beans.property.LongProperty;
导入javafx.beans.property.ObjectProperty;
导入javafx.beans.property.SimpleLongProperty;
导入javafx.beans.property.SimpleObject属性;
导入javafx.concurrent.ScheduledService;
导入javafx.concurrent.Task;
导入javafx.geometry.Pos;
导入javafx.scene.scene;
导入javafx.scene.chart.LineChart;
导入javafx.scene.chart.NumberAxis;
导入javafx.scene.chart.XYChart.Data;
导入javafx.scene.chart.XYChart.Series;
导入javafx.scene.control.ContentDisplay;
导入javafx.scene.control.Label;
导入javafx.scene.image.ImageView;
导入javafx.scene.image.PixelWriter;
导入javafx.scene.image.WritableImage;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.layout.Pane;
导入javafx.scene.layout.StackPane;
导入javafx.scene.layout.VBox;
导入javafx.scene.paint.Color;
导入javafx.stage.Screen;
导入javafx.stage.stage;
导入javafx.util.Duration;
公共类ConstantlyOpenWindows扩展了应用程序{
@凌驾
公共无效开始(阶段primaryStage){
ScheduledService服务=周期性ShowNewWindow();
窗格根=createMemoryMonitor();
初始阶段。设置场景(新场景(根,800600));
primaryStage.show();
service.start();
}
private ScheduledService periodicallyShowNewWindow(){
Screen=Screen.getPrimary();
double maxX=screen.getBounds().getMaxX();
AtomicInteger计数=新的AtomicInteger();
ObjectProperty visibleStage=新的SimpleObjectProperty();
ScheduledService=新的ScheduledService(){
@凌驾
受保护的任务createTask(){
任务=新任务(){
@凌驾
公共整数调用(){
返回count.incrementAndGet();
}
};
返回任务;
}
};
service.setOnSucceeded(事件->{
Stage lastStage=visibleStage.get();
Stage=createWindowWithImage(service.getValue());
visibleStage.set(舞台);
stage.setX(maxX-480);
stage.show();
if(lastStage!=null){
lastStage.close();
}
//gc();
});
服务设置周期(持续时间秒(1));
回程服务;
}
专用阶段createWindowWithImage(整数计数){
阶段=新阶段();
ImageView image=createImage();
标签=新标签(“窗口”+计数);
标签。设置图形(图像);
label.setContentDisplay(ContentDisplay.BOTTOM);
舞台场景(新场景(新StackPane(标签),480500);
返回阶段;
}
私有ImageView createImage(){
WritableImage img=新的WritableImage(400400);
随机rng=新随机();
int x=rng.nextInt(40);
int y=rng.nextInt(40);
PixelWriter pw=img.getPixelWriter();
对于(int i=0;i<400;i++){
对于(int j=0;j<400;j++){
如果(i>=x*10&&i<(x+1)*10&&j>=y*10&&j<(y+1)*10){
设置颜色(i,j,颜色。矢车菊蓝);
}否则{
设置颜色(i,j,颜色。仿古白色);
}
}
}
返回新的ImageView(img);
}
专用窗格createMemoryMonitor(){
LongProperty totalMemory=新的SimpleLongProperty(Runtime.getRuntime().totalMemory());
LongProperty freeMemory=新的SimpleLongProperty(Runtime.getRuntime().freeMemory());
LongProperty maxMemory=新的SimpleLongProperty(Runtime.getRuntime().maxMemory());
NumberBinding usedMemory=总内存。减法(空闲内存);
Label usedMemoryLabel=新标签();
usedMemoryLabel.textProperty().bind(usedMemory.as