70

JavaのWeakHashMapは、キャッシングに役立つとよく言われます。弱参照が値ではなくマップのキーに関して定義されているのは奇妙に思えます。つまり、それは私がキャッシュしたい値であり、キャッシュ以外の誰もがそれらを強く参照していない場合にガベージコレクションを取得したいのですよね?

キーへの弱参照を保持するのにどのように役立ちますか?を実行する場合はExpensiveObject o = weakHashMap.get("some_key")、呼び出し元が強力な参照を保持しなくなるまでキャッシュを「o」に保持する必要があり、文字列オブジェクト「some_key」についてはまったく気にしません。

私は何かが足りないのですか?

4

4 に答える 4

124

WeakHashMap、少なくともほとんどの人が考えるように、キャッシュとしては役に立ちません。あなたが言うように、それは弱いではなく弱いキーを使用するので、ほとんどの人がそれを使用したい目的のために設計されていません(そして実際、私は人々がそれを間違って使用するのを見ました)。

WeakHashMapは、ライフサイクルを制御できないオブジェクトに関するメタデータを保持するのに最も役立ちます。たとえば、クラスを通過する多数のオブジェクトがあり、それらがスコープ外になったときに通知を受ける必要がなく、オブジェクトを参照せずにそれらに関する追加データを追跡したい場合です。

簡単な例(および以前に使用した例)は、次のようになります。

WeakHashMap<Thread, SomeMetaData>

システム内のさまざまなスレッドが実行していることを追跡できる場所。スレッドが終了すると、エントリはマップからサイレントに削除され、スレッドへの最後の参照である場合、スレッドがガベージコレクションされるのを防ぐことはできません。次に、そのマップのエントリを繰り返し処理して、システム内のアクティブなスレッドについてどのメタデータがあるかを確認できます。

キャッシュではなくWeakHashMapを参照してください!詳細については。

必要なキャッシュのタイプについては、専用のキャッシュシステム(EHCacheなど)を使用するか、GuavaMapMakerクラスを確認してください。何かのようなもの

new MapMaker().weakValues().makeMap();

あなたが求めていることを実行します、またはあなたが空想を得たいならば、あなたは時限満了を加えることができます:

new MapMaker().weakValues().expiration(5, TimeUnit.MINUTES).makeMap();
于 2009-11-26T10:32:28.837 に答える
39

の主な用途WeakHashMapは、キーが消えたときに消えたいマッピングがある場合です。キャッシュは逆です---値が消えたときに消えたいマッピングがあります。

キャッシュの場合、必要なのはですMap<K,SoftReference<V>>。メモリが不足すると、 ASoftReferenceはガベージコレクションされます。(これを、WeakReference参照先へのハード参照がなくなるとすぐにクリアされる可能性のある、と比較してください。)キャッシュ内で参照をソフトにする必要があります(少なくとも、Key-Valueマッピングが古くならない場合)。 )、それ以降、後でそれらを探す場合、値がまだキャッシュに残っている可能性があります。代わりに参照が弱い場合、値はすぐにgcされ、キャッシュの目的が無効になります。

便宜上、実装SoftReference内の値を非表示にMapして、キャッシュが。<K,V>ではなくタイプであるように見せることができます<K,SoftReference<V>>。あなたがそれをしたいのなら、この質問はネット上で利用可能な実装のための提案を持っています。

SoftReferenceで値を使用する場合は、クリアされたキーと値のペアを手動で削除する必要があることMap注意してください。そうしないと、サイズが永久に大きくなり、メモリがリークします。SoftReferencesMap

于 2009-11-26T11:43:20.527 に答える
8

考慮すべきもう1つのことは、Map<K, WeakReference<V>>アプローチを採用すると、値が消える可能性がありますが、マッピングは消えないということです。使用法によっては、結果として、弱参照がGCされた多くのエントリを含むマップが作成される場合があります。

于 2011-06-20T19:35:56.013 に答える
7

2つのマップが必要です。1つはキャッシュキーと弱い参照値の間でマップし、もう1つは弱い参照値とキーの間で反対方向にマップします。また、参照キューとクリーンアップスレッドが必要です。

弱参照には、参照されたオブジェクトにアクセスできなくなったときに、参照をキューに移動する機能があります。このキューは、クリーンアップスレッドによって排出される必要があります。 また、クリーンアップには、参照用のキーを取得する必要があります。 これが、2番目のマップが必要な理由です。

次の例は、弱参照のハッシュマップを使用してキャッシュを作成する方法を示しています。プログラムを実行すると、次の出力が得られます。

$ javac -Xlint:unchecked Cache.java && java Cache
{偶数:[2、4、6]、奇数:[1、3、5]}
{偶数:[2、4、6]}

1行目は、オッズリストへの参照が削除される前のキャッシュの内容を示し、2行目はオッズが削除された後のキャッシュの内容を示しています。

これはコードです:

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class Cache<K,V>
{
    ReferenceQueue<V> queue = null;
    Map<K,WeakReference<V>> values = null;
    Map<WeakReference<V>,K> keys = null;
    Thread cleanup = null;

    Cache ()
    {
        queue  = new ReferenceQueue<V>();
        keys   = Collections.synchronizedMap (new HashMap<WeakReference<V>,K>());
        values = Collections.synchronizedMap (new HashMap<K,WeakReference<V>>());
        cleanup = new Thread() {
                public void run() {
                    try {
                        for (;;) {
                            @SuppressWarnings("unchecked")
                            WeakReference<V> ref = (WeakReference<V>)queue.remove();
                            K key = keys.get(ref);
                            keys.remove(ref);
                            values.remove(key);
                        }
                    }
                    catch (InterruptedException e) {}
                }
            };
        cleanup.setDaemon (true);
        cleanup.start();
    }

    void stop () {
        cleanup.interrupt();
    }

    V get (K key) {
        return values.get(key).get();
    }

    void put (K key, V value) {
        WeakReference<V> ref = new WeakReference<V>(value, queue);
        keys.put (ref, key);
        values.put (key, ref);
    }

    public String toString() {
        StringBuilder str = new StringBuilder();
        str.append ("{");
        boolean first = true;
        for (Map.Entry<K,WeakReference<V>> entry : values.entrySet()) {
            if (first)
                first = false;
            else
                str.append (", ");
            str.append (entry.getKey());
            str.append (": ");
            str.append (entry.getValue().get());
        }
        str.append ("}");
        return str.toString();
    }

    static void gc (int loop, int delay) throws Exception
    {
        for (int n = loop; n > 0; n--) {
            Thread.sleep(delay);
            System.gc(); // <- obstinate donkey
        }
    }

    public static void main (String[] args) throws Exception
    {
        // Create the cache
        Cache<String,List> c = new Cache<String,List>();

        // Create some values
        List odd = Arrays.asList(new Object[]{1,3,5});
        List even = Arrays.asList(new Object[]{2,4,6});

        // Save them in the cache
        c.put ("odd", odd);
        c.put ("even", even);

        // Display the cache contents
        System.out.println (c);

        // Erase one value;
        odd = null;

        // Force garbage collection
        gc (10, 10);

        // Display the cache again
        System.out.println (c);

        // Stop cleanup thread
        c.stop();
    }
}
于 2013-06-07T19:02:43.380 に答える