これらの2つの質問に対する簡単な答えは見つかりませんでした。
プロパティインスタンスを削除する前にリスナーを削除する必要がありますか(リスナーは他の場所では使用されません)?
BooleanProperty bool = new SimpleBooleanProperty();
bool.addListener(myListener);
bool.removeListener(myListener); // is it necessary to do this?
bool = null;
プロパティインスタンスを削除する前に、単方向の境界プロパティのバインドを解除する必要がありますか?
BooleanProperty bool = new SimpleBooleanProperty();
bool.bind(otherBool);
bool.unbind(); // is it necessary to do this?
bool = null;
myListener
が「他の場所では使用されていない」ため、[method-]ローカル変数であるとすると、答えはnoです。 。ただし、一般的なケースでは、答えはほとんどnoですが、yesになることもあります。
myListener
が強力に到達可能である限り、ファイナライズの対象になることはなく、メモリを消費し続けます。たとえば、これはmyListener
が「通常」宣言されたstatic
変数である場合に当てはまります(* Javaのすべての「通常」参照はstrong参照*)。ただし、myListener
がローカル変数の場合、現在のメソッド呼び出しとbool.removeListener(myListener)
が戻った後、オブジェクトに到達できなくなります。オブザーバーとObservable
の両方が範囲外になり、最終的には最終決定されます。私自身からの引用 ブログ投稿 この回答についてはもっとうまくいくかもしれません画像:
箱を海に投げても、箱の中にいる猫のことを知っているかどうかは関係ありません。ボックスに到達できない場合、猫も到達できません。
ここでの状況を完全に理解するには、Javaオブジェクト( ソース )のライフサイクルを思い出す必要があります。
オブジェクトは、参照オブジェクトをトラバースせずに何らかのスレッドで到達できる場合、強力に到達可能です。新しく作成されたオブジェクトは、それを作成したスレッドから強力に到達可能です。 [..]オブジェクトは、強く[..]到達可能ではないが、弱い参照をトラバースすることで到達できる場合、弱く到達可能です。弱到達可能オブジェクトへの弱参照がクリアされると、オブジェクトはファイナライズの対象になります。
静的変数の場合、クラスがロードされている限り、これらは常にアクセス可能であり、したがって到達可能です。静的参照がガベージコレクターの仕事を妨げるものになりたくない場合は、代わりに WeakReference
を使用するように変数を宣言できます。 JavaDocによると:
弱参照オブジェクト[..]は、それらの指示対象がファイナライズ可能になり、ファイナライズされてから再利用されることを妨げません。 [..]ガベージコレクターが、ある時点でオブジェクトが弱く到達可能であると判断したとします。その時点で、そのオブジェクトへのすべての弱参照をアトミックにクリアします[..]。同時に、以前は到達可能性が低かったすべてのオブジェクトをファイナライズ可能として宣言します。
説明のために、JavaFXスペースシミュレーションゲームを作成するとします。 Observable
惑星が宇宙船オブザーバーの視界に移動するたびに、ゲームエンジンは宇宙船を惑星に登録します。惑星が見えなくなるときはいつでも、ゲームエンジンは Observable.removeListener()
を使用して、惑星のオブザーバーとしての宇宙船も削除する必要があることは明らかです。そうでなければ、宇宙船が宇宙を飛行し続けると、メモリがリークします。最終的に、ゲームは50億個の観測された惑星を処理できなくなり、 OutOfMemoryError
でクラッシュします。
大多数のJavaFXリスナーとイベントハンドラーのライフサイクルはObservable
のライフサイクルと並行しているため、アプリケーション開発者は何も心配する必要がないことに注意してください。たとえば、 TextField
を作成し、ユーザー入力を検証するリスナーをテキストフィールドのtextProperty
に登録します。テキストフィールドがくっついている限り、リスナーにくっついてもらいたいのです。遅かれ早かれ、テキストフィールドは使用されなくなり、ガベージコレクションが行われると、検証リスナーもガベージコレクションされます。
スペースシミュレーションの例を続けるために、私たちのゲームではマルチプレイヤーのサポートが制限されており、すべてのプレイヤーがお互いを観察する必要があると仮定します。おそらく、各プレイヤーはキルメトリックのローカルスコアボードを保持しているか、ブロードキャストされたチャットメッセージを監視する必要があります。理由はここでは重要なポイントではありません。プレイヤーがゲームを終了するとどうなりますか?明らかに、リスナーが明示的に管理されていない場合(削除)、終了したプレーヤーはファイナライズの対象になりません。他のプレイヤーはオフラインプレイヤーへの強い参照を維持します。リスナーを明示的に削除することは依然として有効なオプションであり、おそらく私たちのゲームにとって最も好ましい選択ですが、少し邪魔になり、より洗練された解決策を見つけたいとしましょう。
ゲームエンジンは、オンラインである限り、すべてのプレーヤーをオンラインで強く参照し続けることを私たちは知っています。したがって、ゲームエンジンが強力な参照を保持している限り、宇宙船が互いの変更やイベントをリッスンするようにします。 「理論」のセクションを読むと、確かにWeakReference
は解決策のように聞こえます。
ただし、WeakReferenceで何かをラップするだけでは、ソリューション全体ではありません。それはめったにありません。 「指示対象」への最後の強い参照がnull
に設定されているか、そうでなければ到達不能になった場合、その指示対象はガベージコレクションの対象になります(SoftReference
)を使用して到達しました。しかし、WeakReferenceはまだぶらぶらしています。アプリケーション開発者は、WeakReference自体が配置されたデータ構造から削除されるように、配管を追加する必要があります。そうでない場合は、メモリリークの重大度を軽減した可能性がありますが、動的に追加された弱いため、メモリリークは引き続き存在します。参照もメモリを消費します。
私たちにとって幸運なことに、JavaFXは「自動削除」のメカニズムとしてインターフェース WeakListener
とクラス WeakEventHandler
を追加しました。関連するすべてのクラスのコンストラクターは、クライアントコードによって提供される実際のリスナー/ハンドラーを受け入れますが、弱参照を使用してリスナー/ハンドラーを格納します。
WeakEventHandler
のJavaDocを見ると、クラスがEventHandler
を実装していることがわかります。したがって、WeakEventHandlerは、EventHandlerが予期される場所であればどこでも使用できます。同様に、WeakListener
の既知の実装はInvalidationListener
またはChangeListener
が予想される場所ならどこでも使用できます。
WeakEventHandler
のソースコードを調べると、クラスは基本的にラッパーにすぎないことがわかります。彼の指示対象(実際のイベントハンドラー)がガベージコレクションされると、WeakEventHandler
は何もしないことで「動作を停止」します WeakEventHandler.handle()
が呼び出されます。 WeakEventHandler
は、彼がどのオブジェクトに接続されているかを知りません。また、接続されたとしても、イベントハンドラーの削除は均一ではありません。ただし、WeakListener
のすべての既知の実装クラスには競争上の優位性があります。コールバックが呼び出されると、それらが登録されているObservable
への参照が暗黙的または明示的に提供されます。したがって、WeakListener
の指示対象がガベージコレクションされると、最終的にWeakListener
実装は、WeakListener
自体がObservable
から削除されることを確認します。
まだ明確でない場合、私たちの宇宙シミュレーションゲームの解決策は、ゲームエンジンにすべてのオンライン宇宙船への強力な参照を使用させることです。宇宙船がオンラインになると、他のすべてのオンライン宇宙船は、 WeakInvalidationListener
などの弱いリスナーを使用して新しいプレーヤーに登録されます。プレーヤーがオフラインになると、ゲームエンジンはプレーヤーへの強い参照を削除し、プレーヤーはガベージコレクションの対象になります。ゲームエンジンは、他のプレーヤーのリスナーとしてオフラインプレーヤーを明示的に削除することを気にする必要はありません。
いいえ。次に何を言うかをよりよく理解するために、最初に私のケース1の回答を読んでください。
BooleanPropertyBase
otherBool
への強力な参照を格納します。これ自体がotherBool
に常に到達可能になるわけではないため、メモリリークが発生する可能性があります。 bool
が到達不能になると、格納されているすべての参照も到達不能になります(他の場所に格納されていない場合)。
BooleanPropertyBase
は、バインドするプロパティのObserver
として自分自身を追加することによっても機能します。ただし、これは、私のケース1の回答で説明したWeakListener
sとほぼ同じように機能するクラスに自分自身をラップすることによって行われます。したがって、bool
を無効にすると、otherBool
から削除されるのは時間の問題になります。
私はケース1の答えに完全に同意しますが、ケース2はもう少しトリッキーです。 The bool.unbind()
呼び出しが必要です。省略した場合、は小さなメモリリークを引き起こします。
次のループを実行すると、アプリケーションは最終的にメモリ不足になります。
BooleanProperty p1 = new SimpleBooleanProperty();
while(true) {
BooleanProperty p2 = new SimpleBooleanProperty();
p2.bind(p1)
}
BooleanPropertyBaseは、通常、実際のWeakListener( WeakListener インターフェイスの実装)を使用せず、中途半端なソリューションを使用しています。すべての「p2」インスタンスは最終的にガベージコレクションされますが、空のWeakReferenceを保持しているリスナーは、「p2」ごとに永久にメモリに残ります。 BooleanPropertyBaseだけでなく、すべてのプロパティにも同じことが当てはまります。 ここで説明されています 詳細に、Java 9で修正されていると言われています。
ほとんどの場合、このメモリリークは、バインド解除されていないすべてのバインディングに対して数十バイトしか残らないため、気付かないでしょう。しかし、場合によっては、それは私に本当の問題を引き起こしました。良い例は、頻繁に更新されるテーブルのテーブルセルです。その後、セルは常に異なるプロパティに再バインドされ、メモリ内のこれらの残りはすぐに蓄積されます。