当 ChangeListener 被输入为 lambda 时,JavaFX 中的 addListener 是否会被垃圾收集?

jav*_*ons 1 java memory garbage-collection javafx listener

我意识到我的项目使用了很多 JavaFXChangeListeners来跟踪某些条件。

我很确定在使用 with 时侦听器不会被垃圾收集property.addListener(ChangeListener),但是如果我将我的侦听器置于 lambda 形式而不是首先声明它们,它们仍然不会被垃圾收集吗?

例子:

        field.focusedProperty().addListener((obs, oldVal, newVal) -> {
          // Do stuff
          }
        });
Run Code Online (Sandbox Code Playgroud)

附带说明一下,当我关闭程序时,所有侦听器都会被清除还是继续存在?

Sla*_*law 8

监听器会被垃圾收集吗?

只要注册到 observable 的侦听器注册到 observable 的侦听器,就不会被垃圾收集。当然,假设有对 observable 的强烈引用。这是因为 observable 对注册的每个侦听器都有一个强引用。事实上,如果您不小心,这可能是您的应用程序内存泄漏的原因。这就是为什么我们有:

弱听者

为了使您的应用程序更能抵抗内存泄漏,您可以使用WeakListener. 为您提供了五种公共实现:

  1. WeakInvalidationListener
  2. WeakChangeListener
  3. WeakListChangeListener
  4. WeakSetChangeListener
  5. WeakMapChangeListener

这些实现包装了它们相应的侦听器并将其保存在WeakReference. 这样 observable 只对弱侦听器有很强的引用,而不是“真正的”侦听器。这就是为什么你必须在需要时自己保持对“真实”侦听器的强引用,否则它会过早被垃圾收集。

例子

这是使用弱侦听器的示例:

import javafx.beans.value.ChangeListener;
import javafx.beans.value.WeakChangeListener;
import javafx.scene.Node;

public class Foo {
  
  // strong reference to "real" listener
  private final ChangeListener<Boolean> focusListener =
      (obs, oldVal, newVal) -> {
        // do something...
      };

  private final Node node;

  public Foo(Node node) {
    this.node = node;
    node.focusedProperty().addListener(new WeakChangeListener<>(focusListener));
  }
}
Run Code Online (Sandbox Code Playgroud)

如果您需要能够随意删除弱侦听器,那么您还需要保留对它的引用。

何时使用弱监听器

当您不能保证不再需要时将删除侦听器时,您应该使用弱侦听器。如果侦听器捕获对其他对象的引用(例如Foo上面示例中的实例),这一点尤其重要。

如果 observable 和 listener 保证在相对相同的时间被垃圾收集,请不要使用弱侦听器。例如,如果Foo有一个属性并且将一个侦听器添加到该属性本身(例如this.someProperty().addListener((...) -> {...})),那么当Foo实例被垃圾回收时,该属性以及侦听器将被垃圾回收。

此外,如果出现以下情况,使用弱监听器可能会比它们的价值更费力:

  • 您的应用程序是微不足道的或相对短暂的。
  • 您的应用程序是相对静态的(即不会连续创建和丢弃您注册侦听器的对象)。

如果不确定,您总是可以走非弱路线开始,只有在您分析问题时才更改为弱听众。

事件处理程序

此处描述的所有内容都适用于EventHandlerWeakEventHandler。请注意,后者没有实现WeakListener.

Lambda 表达式

使用 lambda 表达式来实现侦听器(或处理程序)会改变什么吗?与此上下文没有任何关系。一个 lambda 表达式只是一个函数接口的实现。通过 lambda 表达式创建的实例与其他任何实例一样。

应用程序退出时会发生什么?

当该进程退出时,分配给该进程的所有内存都将释放回操作系统。这意味着所有对象都不再存在于内存中。

  • 垃圾收集的工作原理在理论上看似简单(但实现却很复杂)。基本上有一个由应用程序创建的对象引用图。该图有一个或多个根(例如线程对象)。垃圾收集器遍历图表,它无法到达的每个对象都有资格进行垃圾收集。因此,当且仅当没有其他对侦听器的强引用时,侦听器才会与可观察对象一起被垃圾收集。上面“别打扰...”段落就是这种情况。 (2认同)