如果我想在收集垃圾后自动从
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);
}
}
我希望,按“清除”按钮后,更改侦听器将被从属性中删除,并且不会再被调用了。但没有删除。
如果可能的话,任何人都应该说怎么做?
使用
WeakListener
的实现正是您的做法。该接口的所有标准实现将在收集垃圾后调用,将删除自身。据我所知,界面和实现都不是这种行为。
您的测试问题是
System::gc
只是一个建议。它不能保证垃圾收集器实际上运行,更不用说实际上会收集任何东西了。从该方法的文档中:
runs在Java Virtual Machine中的垃圾收集器。
值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节点)捕获为状态。