清洁JavaFX的属性侦听和绑定(内存泄漏) [英] Clean JavaFX property listeners and bindings (memory leaks)

查看:817
本文介绍了清洁JavaFX的属性侦听和绑定(内存泄漏)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

1,我必须删除属性实例之前移除侦听器(侦听器不使用其他任何地方)?

  BooleanProperty布尔=新SimpleBooleanProperty();
bool.addListener(myListener的);
bool.removeListener(myListener的); //是否有必要这么做吗?
布尔= NULL;

2,我必须删除属性实例之前解除单向界的财产?

  BooleanProperty布尔=新SimpleBooleanProperty();
bool.bind(otherBool);
bool.unbind(); //是否有必要这么做吗?
布尔= NULL;


解决方案

案例1

由于 myListener的不使用其他任何地方,因此我认为,一个[于方法]局部变量,答案是 。虽然在一般情况下,答案多半是的没有的,但有时会出现一个的

只要 myListener的强烈可达,那么它永远不会成为资格审定,并会继续消耗内存。例如,这将是情况下,如果 myListener的是一个正常的,宣布静态变量(*所有的正常在Java中引用的强烈的引用*)。但是,如果 myListener的是一个局部变量,那么当前的方法调用和 bool.removeListener(myListener的回归再之后的对象将是不可到达)是过度设计有点意义的。无论是观察者和观察到的超出范围,并最终敲定。从我自己的博客文章这个答案引述可能描绘出更好的图片


  

这并不重要,如果箱子了解它里面的猫,如果你
  扔箱子入海洋。如果该框是不可达的,也不是
  猫。


理论

要充分这里了解情况,我们必须提醒Java对象的生命周期(<一的自己href=\"http://docs.oracle.com/javase/8/docs/api/java/lang/ref/package-summary.html#reachability\">source):


  

对象是强可到达,如果它可以通过一些线索来达到
  没有任何遍历参考对象。新创建的对象
  通过创建它的线程强可到达。 [..]的目的是
  弱可如果是[不]强烈[..]可到达,但可以
  通过遍历弱引用到达。当一个弱引用
  弱可及对象被清除,该对象就符合
  定稿。


在静态变量的情况下,这些将始终被访问,只要类被装载,从而可到达的。如果我们不想要一个静态引用是阻碍垃圾收集器做他的工作的人,那么我们就可以声明变量使用<一个href=\"http://docs.oracle.com/javase/8/docs/api/java/lang/ref/WeakReference.html\"><$c$c>WeakReference代替。 JavaDoc的说:


  

弱引用对象[..]从做暂时不prevent其指示
  做终结,并被终结,然后被回收。 [..]假设
  垃圾收集器确定在某一时间点上,一个对象
  是弱可到达。当时它会以原子清楚所有弱
  对该对象的引用[..]。同时,将所有申报
  以前的弱可达的对象是终结。


明确管理

有关说明,让我们假设我们写一个JavaFX的空间模拟游戏。每当观察到的行星移动到飞船观察员认为,游戏引擎注册与地球的飞船。这是很明显的,每当地球熄灭来看,游戏引擎也应该使用Observable.removeListener().否则,飞船继续通过空间飞行,内存将泄漏。最终,本场比赛不能处理500十亿观测行星,它会与<崩溃href=\"http://docs.oracle.com/javase/8/docs/api/java/lang/OutOfMemoryError.html\"><$c$c>OutOfMemoryError.

请注意,对于绝大多数的JavaFX侦听器和事件处理程序,他们的生命周期是平行的,他们观察到的,所以应用程序开发人员有什么可担心的。例如,我们可以构造一个 文本字段 以及与文本字段的 textProperty 是验证用户输入的侦听器注册。只要文本字段枝左右,我们希望听者留下来。迟早,文本字段不再使用,并收集垃圾时,他,验证侦听器也被垃圾收集。

自动管理

要继续在太空模拟例子,假设我们的游戏有限多人的支持,所有的玩家需要观察对方。也许每个玩家击杀守指标的本地记分牌或者他们需要观察广播的聊天信息。原因是不是这里的重要的一点。当玩家退出游戏,会发生什么?显然,如果听众没有明确管理(的删除的),那么谁退出不会成为资格审定的球员。其他玩家将保持很强的参考离线播放器。明确去除听众仍然会为我们的游戏的有效选项,并可能是最preferred的选择,但让我们说,感觉有点突兀,我们希望找到一个更漂亮的解决方案。

我们知道,游戏引擎保持强有力的推荐给所有在线玩家,只要他们是在网上。因此,我们希望飞船听更改或只为只要在游戏引擎保持强引用对方的事件。如果你看过理论部分,那么可以肯定一个的WeakReference 听起来像是一个解决方案。

然而,只是在一个WeakReference的包装的东西是不完整的解决方案。但很少有人做得到。的确,当最后一个强引用了参照物设置为或以其它方式无法到达,指涉将符合垃圾收集(假设指涉不能使用达到<一个href=\"http://docs.oracle.com/javase/8/docs/api/java/lang/ref/SoftReference.html\">SoftReference).但WeakReference的仍然是游逛,应用程序开发人员需要添加一些管道,以使WeakReference的本身是从他被放置在数据结构中删除,如果没有的话,我们可能已经降低了内存泄漏的严重程度,但内存泄漏将仍然是present因为动态添加的弱引用消耗内存了。

我们是幸运的,JavaFX的添加界面<一个href=\"http://docs.oracle.com/javase/8/javafx/api/javafx/beans/WeakListener.html\"><$c$c>WeakListener和类<一href=\"http://docs.oracle.com/javase/8/javafx/api/javafx/event/WeakEventHandler.html\"><$c$c>WeakEventHandler作为自动清除的机制。所有相关类的构造函数接受真正的监听器/处理器由客户code提供,但他们使用的是弱引用存储监听器/处理器。

如果你看看 WeakEventHandler 的JavaDoc中,你会发现,类实现事件处理程序,所以WeakEventHandler可以随时随地一个EventHandler有望被使用。同样,一个已知的实现的 WeakListener 可以用于任何一个 InvalidationListener 的ChangeListener 的预期。

如果你看看 WeakEventHandler 来源$ C ​​$ C,你会发现,这个类基本上只有一个包装。当他的所指(的真正的事件处理程序的)进行垃圾回收时,WeakEventHandler停止工作的没有做任何事情时,<一个href=\"http://docs.oracle.com/javase/8/javafx/api/javafx/event/WeakEventHandler.html#handle-T-\"><$c$c>WeakEventHandler.handle()叫做。该WeakEventHandler不知道哪个对象,他已经迷上了,即使他做到了,拆除的事件处理程序并不均匀。 WeakListener 的所有已知的实现类,虽然有竞争优势。当它们的回调函数被调用,他们或明或暗地向他们注册的观察到的一个参考。那么,垃圾收集WeakListener的指涉时,最终WeakListener实施将确保该WeakListener本身从观察到的删除。

如果它不是已经很清楚,我们的太空模拟游戏的解决办法是让游戏引擎使用到所有在线飞船强引用。当飞船上线,所有其它网络的太空船正在与新的玩家使用弱听者注册,如<一个href=\"http://docs.oracle.com/javase/8/javafx/api/javafx/beans/WeakInvalidationListener.html\"><$c$c>WeakInvalidationListener.当玩家下线后,游戏引擎中删除他的强引用的玩家与玩家将有资格进行垃圾回收。游戏引擎没有理会明确去除离线播放器的其他球员听众。

案例2

:要更好地了解我接下来要说,请先阅读我的情况下1回答。

<一个href=\"http://docs.oracle.com/javase/8/javafx/api/javafx/beans/property/BooleanPropertyBase.html\"><$c$c>BooleanPropertyBase存储的强引用 otherBool 。这本身不会导致 otherBool 来始终是访问,并因此可能导致内存泄漏。当布尔变得不可的,那么做他的所有存储引用(假设他们不存储其他的)。

BooleanPropertyBase也加入了自己作为您绑定他的财产观察员工作。不过,他通过包装自己的作品几乎完全一样的 WeakListener 在我的情况下1回答描述一类这样做。所以一旦你废掉布尔,这将是唯一的一次,他是从 otherBool 删除之前的事。

I haven't found a simple answer for these two questions:

1, do I have to remove a listener before deleting the property instance (the listener is not used anywhere else)?

BooleanProperty bool = new SimpleBooleanProperty();
bool.addListener(myListener);
bool.removeListener(myListener); // is it necessary to do this?
bool = null;

2, do I have to unbind a uni-directional bounded property before deleting the property instance?

BooleanProperty bool = new SimpleBooleanProperty();
bool.bind(otherBool);
bool.unbind(); // is it necessary to do this?
bool = null;

解决方案

Case 1

Given that myListener "is not used anywhere else" and therefore I assume, a [method-] local variable, the answer is no. In the general case though, the answer is mostly a no but can sometimes be a yes.

As long as myListener is strongly reachable, then it will never become eligible for finalization and it will continue to consume memory. For example, this would be the case if myListener is a "normally" declared static variable (*all "normal" references in Java are strong references*). However, if myListener is a local variable, then the object will not be reachable anymore after the return of the current method call and bool.removeListener(myListener) is a bit meaningless over-engineering. Both the observer and the observable goes out of scope and will eventually be finalized. A quote from my own blog post about this answer might paint a better picture:

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.

Theory

To fully understand the situation here, we have to remind ourselves of the life-cycle of a Java object (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.

In the case of static variables, these will always be accessible as long as the class is loaded, thus reachable. If we didn't want a static reference to be the one that hinder the garbage collector to do his job, then we could declare the variable to use a WeakReference instead. JavaDoc says:

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.

Explicit management

For illustration, let's assume that we write a JavaFX space simulation game. Whenever an observable planet moves into the view of a spaceship observer, the game engine register the spaceship with the planet. It is quite apparent that whenever the planet goes out of view, the game engine should also remove the spaceship as an observer of the planet by using Observable.removeListener(). Otherwise, as the spaceship continues to fly through space, memory will leak. Eventually, the game cannot handle five billion observed planets and it will crash with an OutOfMemoryError.

Do note that for the vast majority of JavaFX listeners and event handlers, their life-cycle is parallel to that of their observable so the application developer has nothing to worry about. For example, we might construct a TextField and register with the text field's textProperty a listener that validate user input. As long as the text field sticks around, we want the listener to stick around. Sooner or later, the text field is not used anymore and when he is garbage collected, the validation listener is also garbage collected.

Automatic management

To continue on the space simulation example, assume that our game has limited multiplayer support and all the players need to observe each other. Perhaps each player keep a local score board of kill metrics or perhaps they need to observe broadcasted chat messages. The reason is not the important point here. What would happen when a player quit the game? Clearly, if the listeners are not explicitly managed (removed), then the player who quit will not become eligible for finalization. The other player's will keep a strong reference to the offline player. Explicit removal of the listeners would still be a valid option and probably the most preferred choice for our game, but let's say that it feels a bit obtrusive and we want to find a more slick solution.

We know that the game engine keep strong references to all players online, for as long as they are online. So we want the spaceships to listen for changes or events of each other only for as long as the game engine keep the strong references. If you read the "theory" section, then surely a WeakReference sounds like a solution.

However, just wrapping something in a WeakReference is not the entire solution. It seldom is. It is true that when the last strong reference to the "referent" is set to null or otherwise become unreachable, the referent will be eligible for garbage collection (assuming that the referent cannot be reached using a SoftReference). But the WeakReference is still hanging around. The application developer need to add some plumbing so that the WeakReference itself is removed from the data structure he was put in. If not, then we might have reduced the severity of the memory leak but a memory leak will still be present because dynamically added weak references consume memory too.

Lucky for us, JavaFX added interface WeakListener and class WeakEventHandler as a mechanism for "automatic removal". The constructors of all related classes accept the real listener/handler as provided by client code, but they store the listener/handler using a weak reference.

If you look at the JavaDoc of WeakEventHandler, you'll notice that the class implement EventHandler, so the WeakEventHandler can be used wherever an EventHandler is expected. Likewise, a known implementation of a WeakListener can be used wherever an InvalidationListener or a ChangeListener is expected.

If you look into the source code of WeakEventHandler, you'll notice that the class is basically only a wrapper. When his referent (the real event handler) is garbage collected, the WeakEventHandler "stop working" by not doing anything at all when WeakEventHandler.handle() is called. The WeakEventHandler doesn't know about which object he has been hooked up with, and even if he did, the removal of an event handler is not homogeneous. All known implementing classes of WeakListener has a competitive advantage though. When their callbacks are invoked, they are implicitly or explicitly provided a reference to the observable they are registered with. So when the referent of a WeakListener is garbage collected, eventually the WeakListener implementation will make sure that the WeakListener itself is removed from the observable.

If it is isn't already clear, the solution for our space simulation game would be to let the game engine use strong references to all online spaceships. When a spaceship goes online, all other online spaceships are registered with the new player using a weak listener such as WeakInvalidationListener. When a player goes offline, the game engine remove his strong reference to the player and the player will become eligible for garbage collection. The game engine doesn't have to bother about explicit removal of the offline player as a listener of the other players.

Case 2

No. To better understand what I'll say next, please read my case 1 answer first.

BooleanPropertyBase store a strong reference to otherBool. This in itself does not cause otherBool to always be reachable and thus potentially cause a memory leak. When bool become unreachable, then so do all his stored references (assuming they are not stored anywhere else).

BooleanPropertyBase also work by adding himself as an observer of the property you bind him to. However, he do so by wrapping himself in a class that works almost exactly like the WeakListeners described in my case 1 answer. So once you nullify bool, it will be only a matter of time before he is removed from otherBool.

这篇关于清洁JavaFX的属性侦听和绑定(内存泄漏)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆