binding - 清理JavaFX属性监听器和绑定(bind)(内存泄漏)

标签 binding memory-leaks listener javafx

对于这两个问题,我还没有找到简单的答案:

  • 我是否必须在删除属性实例之前删除侦听器(侦听器未在其他任何地方使用)?
    BooleanProperty bool = new SimpleBooleanProperty();
    bool.addListener(myListener);
    bool.removeListener(myListener); // is it necessary to do this?
    bool = null;
    
  • 是否必须在删除属性实例之前取消绑定(bind)单向有界属性?
    BooleanProperty bool = new SimpleBooleanProperty();
    bool.bind(otherBool);
    bool.unbind(); // is it necessary to do this?
    bool = null;
    
  • 最佳答案

    情况1

    鉴于myListener“未在其他地方使用”,因此我认为是[method-]局部变量,答案是 no 。在一般情况下,答案通常是“否”,但有时可以是"is"。

    只要myListener高度可访问,它就永远不会有资格进行终结处理,并且它将继续消耗内存。例如,如果myListener是一个“正常”声明的static变量(* Java中的所有“正常”引用都是强引用*),情况就是这样。但是,如果myListener是局部变量,则在返回当前方法调用之后,该对象将不再可访问,并且bool.removeListener(myListener)有点过度设计。观察者和Observable都超出范围,最终将被最终确定。引用我自己的blog post关于这个答案的话可能会画出更好的图画:

    It doesn’t matter if the box know about the cat inside of it, if you throw the box into the ocean. If the box isn't reachable, nor is the cat.



    理论

    为了充分了解此处的情况,我们必须提醒自己Java对象(source)的生命周期:

    An object is strongly reachable if it can be reached by some thread without traversing any reference objects. A newly-created object is strongly reachable by the thread that created it. [..] An object is weakly reachable if it is [not] strongly [..] reachable but can be reached by traversing a weak reference. When the weak references to a weakly-reachable object are cleared, the object becomes eligible for finalization.



    对于静态变量,只要加载了类,这些变量就始终可以访问。如果我们不希望静态引用成为阻碍垃圾收集器执行其工作的静态引用,则可以声明该变量以使用 WeakReference 代替。 JavaDoc说:

    Weak reference objects [..] do not prevent their referents from being made finalizable, finalized, and then reclaimed. [..] Suppose that the garbage collector determines at a certain point in time that an object is weakly reachable. At that time it will atomically clear all weak references to that object [..]. At the same time it will declare all of the formerly weakly-reachable objects to be finalizable.



    显式管理

    为了说明,我们假设我们编写了JavaFX空间模拟游戏。每当Observable行星进入 spaceship 观察者的视野时,游戏引擎就会向该行星注册 spaceship 。很明显,每当行星离开视线时,游戏引擎也应该使用 Observable.removeListener() 将飞船作为行星的观察者移除。否则,随着飞船继续在太空中飞行,内存将泄漏。最终,该游戏无法处理50亿个观测到的行星,并且会因 OutOfMemoryError 而崩溃。

    请注意,对于绝大多数JavaFX侦听器和事件处理程序,它们的生命周期与Observable的生命周期平行,因此应用程序开发人员无需担心。例如,我们可以构造一个 TextField 并在文本字段的textProperty中注册一个用于验证用户输入的侦听器。只要文本字段停留在周围,我们就希望听众停留在周围。迟早不会再使用该文本字段,并且当他被垃圾回收时,验证侦听器也将被垃圾回收。

    自动管理

    为了继续进行空间模拟示例,请假设我们的游戏对多人游戏的支持有限,并且所有玩家都需要彼此观察。也许每个玩家都在当地设置了一个杀伤指标计分板,或者也许他们需要观察广播的聊天消息。原因不是这里的重点。玩家退出游戏会发生什么?显然,如果未明确管理(移除)侦听器,则退出的播放器将没有资格进行最终确定。其他玩家将对离线玩家保持强烈的引用。显式删除侦听器仍然是一个有效的选择,并且可能是我们游戏的最优选选择,但是可以说,这有点令人不愉快,我们想找到一个更流畅的解决方案。

    我们知道,只要游戏玩家在线,游戏引擎就会对他们在线保持强烈的引用。因此,我们希望 spaceship 仅在游戏引擎保持强大引用力的情况下侦听彼此的变化或事件。如果您阅读“理论”部分,那么WeakReference听起来肯定是一种解决方案。

    但是,仅将某些内容包装在WeakReference中并不是完整的解决方案。很少。的确,当对“引用对象”的最后一个强引用被设置为null或以其他方式变得不可访问时,该引用对象将有资格进行垃圾回收(假设使用 SoftReference 无法访问该引用对象)。但是WeakReference仍然徘徊。应用程序开发人员需要添加一些管道,以便从其放入的数据结构中删除WeakReference本身。否则,我们可能已经降低了内存泄漏的严重性,但是由于动态添加了弱函数,因此内存泄漏仍然存在引用也会占用内存。

    对我们来说幸运的是,JavaFX添加了接口(interface) WeakListener WeakEventHandler 类作为“自动删除”的机制。所有相关类的构造函数都接受客户端代码提供的真实侦听器/处理程序,但是它们使用弱引用存储侦听器/处理程序。

    如果查看WeakEventHandler的JavaDoc,您会注意到该类实现了EventHandler,因此WeakEventHandler可以在需要EventHandler的任何地方使用。同样,可以在需要WeakListenerInvalidationListener的任何地方使用ChangeListener的已知实现。

    如果查看WeakEventHandler的源代码,您会注意到该类基本上只是一个包装器。当他的参照对象(真实事件处理程序)被垃圾回收时,通过调用 WeakEventHandler 完全不执行任何操作,WeakEventHandler.handle()将“停止工作”。 WeakEventHandler不知道与他挂钩的对象,即使他知道,也无法删除事件处理程序。但是,所有已知的WeakListener实现类都具有竞争优势。调用其回调时,将隐式或显式提供对注册它们的Observable的引用。因此,当WeakListener的引用对象被垃圾回收时,最终WeakListener实现将确保从WeakListener中删除了Observable本身。

    如果不清楚,我们的太空模拟游戏的解决方案将是让游戏引擎对所有在线 spaceship 使用强大的引用。当一艘宇宙飞船上线时,所有其他在线宇宙飞船都使用弱听者(例如 WeakInvalidationListener )向新玩家注册。当玩家下线时,游戏引擎将删除他对玩家的强烈引用,并且该玩家将有资格进行垃圾回收。游戏引擎不必担心将脱机播放器作为其他播放器的侦听器显式删除。

    情况二

    编号为了更好地理解我接下来要说的内容,请先阅读案例1的答案。

    BooleanPropertyBase 存储对otherBool的强引用。这本身并不会导致otherBool始终可访问,因此有可能导致内存泄漏。当bool变得不可访问时,所有已存储的引用也会这样做(假设它们未存储在其他任何地方)。
    BooleanPropertyBase也可以通过将其自身添加为绑定(bind)属性的Observer来工作。但是,它是通过将自己包装在几乎完全类似于我的案例1答案中描述的WeakListener的类中来实现的。因此,一旦使bool无效,将其从otherBool中删除只是一个时间问题。

    关于binding - 清理JavaFX属性监听器和绑定(bind)(内存泄漏),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14558266/

    相关文章:

    wpf - 将 WPF ListBox SelectedItem 绑定(bind)到列表

    ios - CTFontRef 导致内存泄漏

    java - 如何解决 Tomcat 应用程序访问的内存泄漏第 3 方 DLL(无源代码)?

    java - 具有边框布局的 JPanel 上的事件

    javascript - 为什么监听器激活两次?

    jquery - 关闭 jquery 后启用悬停

    WPF 将 TextBlock 绑定(bind)到窗口的标题

    java - 实例变量对静态变量保持强引用是否会导致内存泄漏?

    java - 当另一个类中的 JLabel 更新时如何更新 JLabel?

    java - BeansBinding、JGoodies Binding、JFace 或 Glazed Lists?