案例一
鉴于myListener“未在其他任何地方使用”,因此我假设是一个 [method-] 局部变量,答案是 否。但在一般情况下,答案大多是否,但有时可能是是。
只要myListener 是强可达的,那么它就永远不会有资格进行终结,并且会继续消耗内存。例如,如果myListener 是“正常”声明的static 变量(*Java 中的所有“正常”引用都是strong 引用*),就会出现这种情况。但是,如果myListener是一个局部变量,那么在当前方法调用返回后,对象将不再可达,bool.removeListener(myListener)有点无意义的过度工程。观察者和Observable 都超出了范围,最终将被最终确定。我自己的 blog post 关于这个答案的引用可能会描绘出更好的画面:
盒子是否知道里面的猫并不重要,如果你
把盒子扔进海里。如果无法访问该框,也无法访问该框
猫。
理论
要完全理解这里的情况,我们必须提醒自己Java对象的生命周期(source):
如果某个对象可以被某个线程访问,则它是强可达的
不遍历任何参考对象。一个新创建的对象是
创建它的线程可强烈访问。 [..] 一个对象是
如果 [not] 强烈 [..] 可到达但可以
通过遍历弱引用到达。当弱引用
弱可达对象被清除,对象变得有资格
最终确定。
在静态变量的情况下,只要类被加载,它们就总是可以访问的,因此是可以访问的。如果我们不希望静态引用成为阻碍垃圾收集器完成工作的引用,那么我们可以将变量声明为使用WeakReference。 JavaDoc 说:
弱引用对象 [..] 不会阻止它们的引用对象
最终化,最终化,然后回收。 [..] 假设
垃圾收集器在某个时间点确定一个对象
是弱可达的。到那时它将原子地清除所有弱
对该对象的引用[..]。同时它将宣布所有
以前弱可达对象的数量将被最终确定。
显式管理
为了说明,假设我们编写了一个 JavaFX 空间模拟游戏。每当Observable 行星进入宇宙飞船观察者的视野时,游戏引擎就会将宇宙飞船注册到该行星。很明显,每当行星离开视野时,游戏引擎也应该使用Observable.removeListener() 将宇宙飞船作为行星的观察者移除。否则,随着飞船继续在太空中飞行,内存就会泄漏。最终,游戏无法处理 50 亿颗观测到的行星,它会以OutOfMemoryError 崩溃。
请注意,对于绝大多数 JavaFX 侦听器和事件处理程序,它们的生命周期与 Observable 的生命周期平行,因此应用程序开发人员无需担心。例如,我们可以构造一个TextField 并使用文本字段的textProperty 注册一个验证用户输入的侦听器。只要文本字段存在,我们就希望听众持续存在。迟早,文本字段不再使用,当他被垃圾收集时,验证侦听器也被垃圾收集。
自动管理
继续空间模拟示例,假设我们的游戏对多人游戏的支持有限,并且所有玩家都需要互相观察。也许每个玩家都有一个本地的击杀指标计分板,或者他们可能需要观察广播的聊天消息。原因不是这里的重点。当玩家退出游戏时会发生什么?显然,如果监听器没有被明确管理(移除),那么退出的玩家将没有资格进行最终确定。其他玩家将保持对离线玩家的强烈参考。显式删除侦听器仍然是一个有效的选项,并且可能是我们游戏的最首选选择,但假设它感觉有点突兀,我们希望找到一个更巧妙的解决方案。
我们知道游戏引擎会强烈引用所有在线玩家,只要他们在线。所以我们希望宇宙飞船只在游戏引擎保持强引用的情况下监听彼此的变化或事件。如果您阅读“理论”部分,那么WeakReference 听起来肯定是一个解决方案。
但是,仅将某些内容包装在 WeakReference 中并不是完整的解决方案。它很少是。确实,当对“referent”的最后一个强引用设置为null 或变得无法访问时,该referent 将有资格进行垃圾回收(假设无法使用SoftReference 访问该referent)。但是 WeakReference 仍然存在。应用程序开发人员需要添加一些管道,以便将 WeakReference 本身从他放入的数据结构中删除。如果没有,那么我们可能已经降低了内存泄漏的严重性,但内存泄漏仍然存在,因为动态添加了弱引用也会消耗内存。
幸运的是,JavaFX 添加了接口WeakListener 和类WeakEventHandler 作为“自动删除”的机制。所有相关类的构造函数都接受客户端代码提供的真实监听器/处理程序,但它们使用弱引用存储监听器/处理程序。
如果您查看WeakEventHandler 的JavaDoc,您会注意到该类实现了EventHandler,因此可以在任何需要EventHandler 的地方使用WeakEventHandler。同样,WeakListener 的已知实现可以在任何需要 InvalidationListener 或 ChangeListener 的地方使用。
如果您查看WeakEventHandler 的源代码,您会注意到该类基本上只是一个包装器。当他的所指对象(真正的事件处理程序)被垃圾收集时,WeakEventHandler 在调用WeakEventHandler.handle() 时根本不做任何事情来“停止工作”。 WeakEventHandler 不知道他连接了哪个对象,即使他知道了,事件处理程序的删除也不是同质的。不过,所有已知的WeakListener 实现类都具有竞争优势。当它们的回调被调用时,它们被隐式或显式地提供了对它们注册的Observable 的引用。因此,当WeakListener 的所指对象被垃圾回收时,最终WeakListener 实现将确保WeakListener 本身从Observable 中删除。
如果还不清楚,我们的太空模拟游戏的解决方案是让游戏引擎使用对所有在线宇宙飞船的强引用。当一艘宇宙飞船上线时,所有其他在线宇宙飞船都会使用弱监听器向新玩家注册,例如WeakInvalidationListener。当玩家下线时,游戏引擎会删除他对玩家的强引用,玩家将有资格进行垃圾回收。游戏引擎不必费心将离线玩家显式移除为其他玩家的侦听器。
案例 2
否。为了更好地理解我接下来要说的内容,请先阅读我的案例 1 答案。
BooleanPropertyBase 存储对otherBool 的强引用。这本身不会导致otherBool 始终可访问,因此可能会导致内存泄漏。当bool 变得无法访问时,其所有存储的引用也会这样做(假设它们没有存储在其他任何地方)。
BooleanPropertyBase 也可以通过将自身添加为您绑定到的属性的Observer 来工作。但是,它通过将自身包装在一个几乎与我的案例 1 答案中描述的 WeakListeners 完全相同的类中来实现这一点。因此,一旦您取消了bool,它从otherBool 中删除只是时间问题。