JavaFX - 处理 window
JavaFX - Dispose of window
我在 JavaFX 中有一个 window 我需要 'clean' 即:重置所有字段的值并将其设置回初始化时的状态。我想我可以做类似 window.dispose()
的事情,但事实证明这样的事情似乎并不存在。
这似乎是对 window 引擎的重大疏忽,因为我将打开多个 windows,但我不想坚持下去。如果我这样做 stage.close()
它只会隐藏 window 而不会释放内存,可能会导致内存泄漏。
当您在 window 上调用 close()
(或等效地 hide()
)时,FX 工具包将释放它持有的对 window 的所有引用。因此,只要您不保留对 window 的引用,一旦 close()
被调用,它就有资格进行垃圾回收。在垃圾回收方面,它的行为与任何其他 Java 对象一样 - 一旦垃圾被回收,与 window 关联的资源就会被释放。
这是一个每秒打开一个新 window 的演示,并在打开时关闭前一个 window。 window 在标签中显示图像,因此它消耗了合理的内存块。初级阶段显示内存统计信息(也是每秒更新一次)。这完全符合您的预期,内存逐渐增加,直到垃圾收集器启动,此时内存使用率回落。 (在我的系统上,它在 ~7MB 和 ~65MB 之间循环;您的里程可能因 OS、JDK 版本和系统资源而异。)您可以插入对 System.gc();
的调用onSucceeded
处理程序的末尾(仅用于演示目的;我不建议在实际代码中这样做)如果您想更清楚地看到垃圾收集释放了所有资源。
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);
}
}
我在 JavaFX 中有一个 window 我需要 'clean' 即:重置所有字段的值并将其设置回初始化时的状态。我想我可以做类似 window.dispose()
的事情,但事实证明这样的事情似乎并不存在。
这似乎是对 window 引擎的重大疏忽,因为我将打开多个 windows,但我不想坚持下去。如果我这样做 stage.close()
它只会隐藏 window 而不会释放内存,可能会导致内存泄漏。
当您在 window 上调用 close()
(或等效地 hide()
)时,FX 工具包将释放它持有的对 window 的所有引用。因此,只要您不保留对 window 的引用,一旦 close()
被调用,它就有资格进行垃圾回收。在垃圾回收方面,它的行为与任何其他 Java 对象一样 - 一旦垃圾被回收,与 window 关联的资源就会被释放。
这是一个每秒打开一个新 window 的演示,并在打开时关闭前一个 window。 window 在标签中显示图像,因此它消耗了合理的内存块。初级阶段显示内存统计信息(也是每秒更新一次)。这完全符合您的预期,内存逐渐增加,直到垃圾收集器启动,此时内存使用率回落。 (在我的系统上,它在 ~7MB 和 ~65MB 之间循环;您的里程可能因 OS、JDK 版本和系统资源而异。)您可以插入对 System.gc();
的调用onSucceeded
处理程序的末尾(仅用于演示目的;我不建议在实际代码中这样做)如果您想更清楚地看到垃圾收集释放了所有资源。
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);
}
}