いつWeakHashMapまたはWeakReferenceを使用しますか?


163

弱参照の使用は実装の見たことがないので、それらの使用例とは何か、そして実装がどのように機能するかを理解しようとしています。WeakHashMapまたはを使用する必要WeakReferenceがあったのはいつですか?



回答:


96

強い参照の1つの問題は、特に画像のような非常に大きな構造でのキャッシングです。私が取り組んでいるWebサイト設計ツールのように、ユーザーが提供した画像を処理する必要があるアプリケーションがあるとします。当然、これらのイメージをキャッシュする必要があります。ディスクからロードするのは非常にコストがかかり、メモリ内に(巨大になる可能性のある)イメージの2つのコピーが同時に存在する可能性を避けたいからです。

画像キャッシュは、私たちが絶対に必要としない場合に画像をリロードするのを防ぐことになっているので、キャッシュには常にメモリ内にある画像への参照が常に含まれるべきであることがすぐにわかります。ただし、通常の強力な参照では、その参照自体がイメージをメモリに残します。そのため、イメージがメモリで不要になったときを何らかの方法で判断し、キャッシュから削除して、ガベージコレクションの対象にすることができます。ガベージコレクターの動作を複製し、オブジェクトをメモリ内に置くかどうかを手動で決定する必要があります。

弱い参照の理解、イーサン・ニコラス


43
この場合、SoftReferencesの方が優れているとは言えません。つまり、メモリが不足し始めたときにのみ収集される参照です。
JesperE 2008

少し混乱しています... SWT画像キャッシュがあるとしましょう。SOリソースを解放するには、dispose()メソッドを使用してSWTイメージをDISPOSEDする必要があります。WeakHashMapを使用してそれらを格納する場合、GCがオブジェクトを破棄するかどうかを正確に示しますか?
marcolopes 2014

2
@marcolopes GCは他のオブジェクトでファイナライザを使用します。あなたがそれをするとき、SWTはそれを好きではないようです、それで私はあなたがオペレーティングシステムのリソースをで管理できるとは思いませんWeakHashMap
Jacob Krall、2014

@marcolopes、(私はあなたのGCがメモリを再利用する前にfinalizeへの呼び出しを保証すると仮定します。)キャッシュファイナライザで破棄が行われれば、すべて順調です。disposeを手動で呼び出す必要がある場合は、1)クラスを拡張してファイナライザにdisposeを配置するか、2)ファントムリファレンスを使用して追跡し、それに応じてdisposeを実行します。オプション2の方が優れています(復活のバグを回避し、別のスレッドで破棄を実行する機能を提供します)が、オプション1はヘルパークラスなしで実装する方が簡単です。
Pacerier 2017


55

WeakReferenceSoftReference

明確にすべき1つの違いは、a WeakReferenceとaの違いSoftReferenceです。

基本的に、a WeakReferenceは、参照されるオブジェクトがハード参照を持たなくなると、JVMによって熱心にGC-dされます。一方、Dオブジェクトそれは本当にメモリを再利用するために必要になるまで、ガベージコレクタによって約残される傾向があります。SoftReference

WeakReferences 内に保持されるキャッシュは、ほとんど役に立ちません(で、WeakHashMap弱く参照されるのはキーです)。SoftReferences使用可能なメモリで拡大および縮小できるキャッシュを実装する場合に値をラップするのに便利です。


4
「WeakReferences内に値が保持されているキャッシュは、まったく役に立たないでしょう」私はまったく同意しません。
Thomas Eding、2011年

5
@trinithis-ええと、私は本当に何を言うべきかわかりません。値を参照していないとすぐに値が消えるキャッシュが、なぜ便利なのでしょうか。
oxbow_lakes

4
メモ化などの場合、キャッシュされた値を緩やかに保持するキャッシュが役立ちます。
Thomas Eding、

5
@ThomasEdingまだわかりません。キャッシュが役立つと思われるのは、他にキャッシュへの参照がない場合だけです。キャッシュへの参照がある場合、何のためにキャッシュが必要ですか?
ランチャー2014年

2
@ ThomasEding、Softrefは「メモリがなくなるまでこれを保存する」ことを環境に伝えます。Weakrefは「GCが実行されるまでこれを保存する」ことを環境に伝えます。GC自体をデバッグまたはプロファイリングしない限り、weakrefのユースケースはまったくありません。メモリに依存するキャッシュが必要な場合は、softrefを使用してください。キャッシュが必要ない場合は、キャッシュしないでください。weakrefはどこから入りますか?
パセリエ2017

30

特にWeakReferenceWeakHashMapの一般的な用途の1つは、オブジェクトにプロパティを追加することです。場合によっては、機能またはデータをオブジェクトに追加する必要がありますが、サブクラス化や構成はオプションではありません。その場合、追加したいプロパティに拡張するオブジェクトをリンクするハッシュマップを作成するのが明らかです。 。その後、プロパティが必要なときはいつでも、マップで調べることができます。ただし、プロパティを追加しているオブジェクトが破壊されて大量に作成される傾向がある場合、マップ内の多くの古いオブジェクトが大量のメモリを消費することになります。

WeakHashMap代わりにaを使用すると、オブジェクトはプログラムの残りの部分で使用されなくなるとすぐにマップを離れます。これは望ましい動作です。

私はするいくつかのデータを追加するには、この操作を行う必要がありましたjava.awt.Component(1.4.2と1.5の間JREの変化を回避するために、私はすべてのコンポーネント私が興味を持ってint型だったサブクラス化することで、それを固定している可能性がJButtonJFrameJPanel....)が、これはあまりでしたはるかに少ないコードで簡単に。


1
WeakHashMapは、「プログラムの残りの部分では使用されなくなった」ことをどのようにして知っていますか?
Vinoth Kumar CM、2011

2
弱いhasmapはそのキーに弱い参照を使用します。オブジェクトが弱参照を通じてのみ参照される場合、ガベージコレクターは弱参照(この場合はWeaHashMap)の所有者に「通知」します。これらのオブジェクトがガベージコレクターとどのように相互作用するかを理解するには、javadocsのWeakReferencesとReferenceQueuesを読みます。
luke

1
おかげで、あなたは上記の説明のために簡単なコードを提供できますか?
沸騰したお湯

したがって、一部のクラスを拡張できないJavaの風変わりなAPIのため、weakreference はJavaでのみ意味があります。
パセリエ2017

22

WeakHashMapおよびのもう1つの便利なケースWeakReferenceは、リスナーレジストリの実装です。

特定のイベントをリッスンしたいものを作成するとき、通常はリスナーを登録します、例えば

manager.registerListener(myListenerImpl);

managerリスナーをに保存している場合、これは、WeakReferenceたとえばを使用してレジスタを削除する必要がないことを意味します。manager.removeListener(myListenerImpl)これは、リスナーまたはリスナーを保持しているコンポーネントが利用できなくなると、レジスタが自動的に削除されるためです。

もちろん、手動でリスナーを削除することもできますが、削除しない場合や忘れ場合でも、メモリリークは発生せず、リスナーのガベージコレクションが妨げられることもありません。

どこにWeakHashMap絵が入っていますか?

登録されたリスナーをWeakReferences として格納するリスナーレジストリには、これらの参照を格納するためのコレクションが必要です。WeakHashSet標準のJavaライブラリには実装はありませんWeakHashMapが、後者は簡単に最初のライブラリの機能を「実装」するために使用できます。

Set<ListenerType> listenerSet =
    Collections.newSetFromMap(new WeakHashMap<ListenerType, Boolean>());

これとともに listenerSet新しいリスナーを登録するには、それをセットに追加するだけでよく、明示的に削除されなくても、リスナーが参照されなくなった場合は、JVMによって自動的に削除されます。


10
リスナーリストにweakHashSetsを使用する場合の問題は、register()で作成された匿名リスナーインスタンスが簡単に失われることです。これはユーザーが予期しないことです。代わりにマネージャーからのリスナーへのより強い参照を保持し、代わりに呼び出し元に依存して正しいことを行う方が安全です。
Gunanaresh

リスナーレジストリの実装の場合:登録されたすべてのリスナーは、次のGCキックで収集/破棄されますか?たとえば、すべてのリスナーのonSomethingHappened()メソッドを起動しているときに、GCがキックするとどうなりますか?
ブラックカラ2016年

@icza、私は人々がまだこの神話を売り出しているとは信じられません。これは完全に間違った答えです。オブジェクトのWeakHashMap必要なときはいつでも別の便利なケースと言えるでしょうHashMap。そのため、objがスコープ外になるとアイテムが自動的に削除されるため、hashmap.remove を手動で実行する必要はありません。文字通り魔法!このような醜い魔法のハックは、完全な顔面攻撃です。
Pacerier 2017

3
@Pacerier:私はJavaScriptの他のコメントからのリンクをたどっていますが、WeakMapを使用したリスナーレジストリの実装が神話である理由がよくわかりません。たとえば、WebSocketクライアントをレジストリサービスを介していくつかのリスナーにバインドする必要がある場合、ソケットオブジェクトをWeakMapにキーとして保存し(エラーなどで接続が閉じた後、メモリにハングアップしないようにする)ことができます。必要に応じて、すべてのリスナーを取得します。では、そのアプローチの何が問題になっているのでしょうか。
オーガニックの損傷

1
@Pacerier私もあなたの反対を理解できませんでした。パブリッシュサブスクライブまたはイベントバスのシナリオでは、弱い参照のコレクションは私にとって完全に理にかなっています。サブスクライブするオブジェクトは、正式にサブスクライブを解除する必要なしに、スコープ外に出てガベージコレクションに進むことができます。サブスクライブするオブジェクトの知識なしにサードパーティのオブジェクトが初期サブスクリプションを担当した場合、そのサブスクライブ解除プロセスは特に複雑になる可能性があります。のコレクションによりWeakReference、コードベースが大幅に簡素化され、登録解除に失敗することに関連する不要なバグが回避されます。どのような欠点ですか?
バジルブルク2018年

5

このブログ投稿では、両方のクラスの使用方法を示します。Java:IDで同期する。使い方は次のようになります:

private static IdMutexProvider MUTEX_PROVIDER = new IdMutexProvider();

public void performTask(String resourceId) {
    IdMutexProvider.Mutex mutext = MUTEX_PROVIDER.getMutex(resourceId);
    synchronized (mutext) {
        // look up the resource and do something with it
    }
}

IdMutextProviderは、同期するためのIDベースのオブジェクトを提供します。要件は次のとおりです。

  • 同等のIDを同時に使用するには、同じオブジェクトへの参照を返す必要があります
  • 異なるIDに対して異なるオブジェクトを返す必要があります
  • リリースメカニズムなし(オブジェクトはプロバイダーに返されません)
  • リークしてはなりません(未使用のオブジェクトはガベージコレクションの対象です)

これは、次のタイプの内部ストレージマップを使用して実現されます。

WeakHashMap<Mutex, WeakReference<Mutex>>

オブジェクトはキーと値の両方です。マップの外部にオブジェクトへのハード参照がない場合は、ガベージコレクションすることができます。マップ内の値はハード参照で格納されるため、メモリリークを防ぐために、値をWeakReferenceでラップする必要があります。この最後の点は、javadocで説明されています。


3

たとえば、特定のクラスで作成されたすべてのオブジェクトを追跡したい場合。これらのオブジェクトのガベージコレクションを許可するには、オブジェクト自体ではなく、オブジェクトへの弱参照のリスト/マップを保持します。

誰かがファントム参照を私に説明できたら、私は幸せになります...


2
1つの用途:PhantomReferencesを使用すると、オブジェクトがメモリから削除された時期を正確に判断できます。それらは実際にそれを決定する唯一の方法です。(weblogs.java.net/blog/enicholas/archive/2006/05/...
ヤコブクラール

実際には、明示的にクリアするまで削除されません。「ソフト参照やウィーク参照とは異なり、ファントム参照はキューに入れられたときにガベージコレクターによって自動的にクリアされません。ファントム参照を介して到達可能なオブジェクトは、そのようなすべての参照がクリアされるか、それ自体が到達不能になるまで残ります。」
jontro 2012

@jontro、しかしそれはすでに確定されおり、すべてのメンバーがいなくなっています。事実上、それは空白のオブジェクトです。stackoverflow.com/q/7048767/632951
Pacerier

3

上記のように、弱い参照は強い参照が存在する限り保持されます。

使用例としては、リスナー内でWeakReferenceを使用し、ターゲットオブジェクトへのメイン参照がなくなるとリスナーがアクティブでなくなるようにします。これは、WeakReferenceがリスナーリストから削除されることを意味しないことに注意してください。クリーンアップは引き続き必要ですが、たとえば、スケジュールされた時間に実行できます。これは、リッスンされたオブジェクトが強い参照を保持することを防ぎ、最終的にはメモリの膨張の原因になるという影響もあります。例:ウィンドウよりもライフサイクルが長いモデルを参照するSwing GUIコンポーネント。

上記のようにリスナーと遊んでいると、オブジェクトがユーザーの視点から「即座に」収集されることにすぐに気付きました。


役立つ回答に感謝します。しかし、その場合、リスナーは強く登録(参照)されるべきですか?
ブラックカラ2016年

この答えは完全に間違っています。推敲:stackoverflow.com/questions/154724/...
Pacerier

@Pacerier- WeakReferencesあなたのコメントは完全に間違っています!

2

WeakReferencesで私が実際に使用した例の1つは、めったに使用されない単一の非常に大きなオブジェクトがある場合です。不要なときにメモリに保持したくない。しかし、別のスレッドが同じオブジェクトを必要とする場合、それらのうちの2つをメモリーに入れたくないでしょう。オブジェクトへの弱い参照と、それを使用するメソッドのハード参照を保持できます。メソッドが両方とも終了すると、オブジェクトが収集されます。


1
これはソフトリファレンスであり、弱リファレンスではありません。stackoverflow.com/a/155492/632951
Pacerier


-1

weakhashmapを使用して、拡張オブジェクトの作成のためにリソースフリーのキャッシュを実装できます。

しかし、変更可能なオブジェクトを持つことは望ましくないことに注意してください。クエリの結果(実行に約400ミリ秒かかる)を、ほとんど更新されないテキスト検索エンジンにキャッシュするために使用しました。


あなたは弱参照ではなく、弱参照について話している。stackoverflow.com/a/155492/632951
Pacerier
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.