如何在收集垃圾后自动从财产中自动删除?

问题描述 投票:0回答:1

如果我想在收集垃圾后自动从

ChangeListener
自动取出
Property
。我试图使用
WeakChangeListener
,但它不起作用。这是我的代码:

public class NewMain extends Application {

    private StringProperty property = new SimpleStringProperty("Initial Value");
    private WeakChangeListener<String> weakChangeListener = new WeakChangeListener<>((obs, oldVal, newVal) ->
                System.out.println("Value changed: " + oldVal + " -> " + newVal));

    @Override
    public void start(Stage primaryStage) {
        Button changeButton = new Button("Change Value");
        Button clearButton = new Button("Clear");
        property.addListener(weakChangeListener);
        changeButton.setOnAction(e -> property.set("New Value " + Math.random()));
        clearButton.setOnAction(e -> {
            weakChangeListener = null;
            System.gc();
            System.runFinalization();
        });

        VBox root = new VBox(10, changeButton, clearButton);
        Scene scene = new Scene(root, 300, 200);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
我希望,按“清除”按钮后,更改侦听器将被从属性中删除,并且不会再被调用了。但没有删除。

如果可能的话,任何人都应该说怎么做?

使用
java javafx
1个回答
0
投票
WeakListener

的实现正是您的做法。该接口的所有标准实现将在收集垃圾后调用,将删除自身。据我所知,界面和实现都不是这种行为。

您的测试问题是
System::gc只是一个建议。它不能保证垃圾收集器实际上运行,更不用说实际上会收集任何东西了。从该方法的文档中: runs在Java Virtual Machine中的垃圾收集器。

gc方法表明,Java虚拟机将工作努力用于回收未使用的对象,以使它们当前占用的内存可用于Java Virtual Machine的重复使用。当控制从方法调用返回时,Java虚拟机已尽力从所有未使用的对象中收回空间。不能保证这项工作将回收任何特定数量的未使用对象,回收任何特定数量的空间,或者在方法返回或以往之前的任何特定时间(如果有的话)完成。也不能保证这项工作将确定任何特定数量的对象中可及性的变化,或者将清除和重新定位任何特定数量的

Reference
对象。 您可以尝试的一个事情是在每次迭代之间延迟的循环中调用

System::gc
。这将增加GC运行的机会,但是当然仍然不能保证它会。这是一个例子:

import javafx.application.Application; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.WeakChangeListener; import javafx.event.ActionEvent; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.ToolBar; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; public class Main extends Application { private final IntegerProperty count = new SimpleIntegerProperty(); // You must keep a strong reference to the wrapped listener for as // long as the listener should be in use. If the only reference to // the listener is held by the WeakChangeListener, then it could // be garbage collected prematurely. private ChangeListener<Number> listener; private WeakChangeListener<Number> weakListener; @Override public void start(Stage primaryStage) { var label = new Label("Count: 0"); listener = (_, _, newVal) -> { var text = "Count: %,d".formatted(newVal); label.setText(text); }; weakListener = new WeakChangeListener<>(listener); count.addListener(weakListener); var incrementBtn = new Button("Increment count"); incrementBtn.setOnAction(this::onIncrementCount); var clearBtn = new Button("Clear listener"); clearBtn.setOnAction(this::onClearListener); var toolbar = new ToolBar(incrementBtn, clearBtn); var root = new BorderPane(); root.setTop(toolbar); root.setCenter(label); primaryStage.setScene(new Scene(root, 500, 300)); primaryStage.show(); } private void onIncrementCount(ActionEvent event) { event.consume(); count.set(count.get() + 1); } private void onClearListener(ActionEvent event) { event.consume(); var source = (Node) event.getSource(); source.setDisable(true); listener = null; // Remove all strong references to listener // Call 'gc()' in a loop a certain number of times, or until the // the listener has been garbage collected. There's nothing special // about the number '20' here. It was chosen arbitrarily. Same with // choosing to sleep for '100' milliseconds. // // Note this method is called on the JavaFX Application Thread, so // the application will freeze for as long as this loop runs (up to // 2 seconds in this case). for (int i = 0; i < 20 && !weakListener.wasGarbageCollected(); i++) { System.gc(); try { Thread.sleep(100L); } catch (InterruptedException ignore) { } } // Notify the user of the result of trying to garbage collect the listener. var alert = new Alert(AlertType.INFORMATION); alert.initOwner(source.getScene().getWindow()); alert.setTitle("Clear Listener Result"); alert.setHeaderText(null); if (weakListener.wasGarbageCollected()) { alert.setContentText("Listener was garbage collected."); } else { alert.setContentText("Listener WAS NOT garbage collected!"); source.setDisable(false); // Allow retries } alert.show(); } public static void main(String[] args) { launch(Main.class); } }

使用Eclipse Temurin-23.0.1+11(热点VM,默认垃圾收集器),在按下“清除听众”按钮时,听众是为我收集的垃圾。
除了一边,请注意,自Java 18以来已被拆除。
如果您正在创建一个自定义控件,并且皮肤从

System::runFinalization

产生

SkinBase,那么我建议使用

registerXXXListener
方法,而不是直接使用弱听众。该API为您使用弱听众,并且在处置皮肤时也将去除它们。

1。当然,这意味着如果听众在收集垃圾后从未被调用,则不会自我避难。但这不太可能是一个问题,因为

式听众是一个小物体。重要的是,允许收集垃圾的“真实的听众”(由弱者包裹的人),因为听众可能已将大型物体(例如Javafx节点)捕获为状态。

	
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.