8

MapMaker を使用して、大きなオブジェクトをキャッシュするマップを作成したいと考えています。これは、十分なメモリがない場合はキャッシュから削除する必要があります。この小さなデモ プログラムは問題なく動作するようです。

public class TestValue {
    private final int id;
    private final int[] data = new int[100000];

    public TestValue(int id) {
        this.id = id;
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalized");
    }  
}  


public class Main {

    private ConcurrentMap<Integer, TestValue> cache;
    MemoryMXBean memoryBean;

    public Main() {
        cache = new MapMaker()
                .weakKeys()
                .softValues()
                .makeMap();
        memoryBean = ManagementFactory.getMemoryMXBean();
    }

    public void test() {
        int i = 0;
        while (true) {
            System.out.println("Etntries: " + cache.size() + " heap: "  
                + memoryBean.getHeapMemoryUsage() + " non-heap: "  
                + memoryBean.getNonHeapMemoryUsage());
            for (int j = 0; j < 10; j++) {
                i++;
                TestValue t = new TestValue(i);
                cache.put(i, t);
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException ex) {
            }
       }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Main m = new Main();
        m.test();
    }

}

ただし、実際のアプリケーションで同じことを行うと、エントリは基本的に追加されるとすぐにキャッシュから削除されます。私の実際のアプリケーションでは、整数もキーとして使用します。キャッシュされた値は、データを含むディスクから読み取られたアーカイブ ブロックです。私が理解している限り、弱参照は使用されなくなるとすぐにガベージコレクションされるため、キーが弱参照であるため、これは理にかなっているようです。次のようにマップを作成すると:

    data = new MapMaker()
            .softValues()
            .makeMap();

エントリがガベージ コレクションされることはなく、テスト プログラムでメモリ不足エラーが発生します。TestValue エントリの finalize メソッドは呼び出されません。テスト方法を次のように変更すると:

public void test() {
    int i = 0;
    while (true) {
        for (final Entry<Integer, TestValue> entry :
            data.entrySet()) {
            if (entry.getValue() == null) {
                data.remove(entry.getKey());
            }
        }
        System.out.println("Etntries: " + data.size() + " heap: "
            + memoryBean.getHeapMemoryUsage() + " non-heap: "  
            + memoryBean.getNonHeapMemoryUsage());
        for (int j = 0; j < 10; j++) {
            i++;
            TestValue t = new TestValue(i);
            data.put(i, t);
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException ex) {
        }
    }
}

エントリがキャッシュから削除され、TestValue オブジェクトのファイナライザーが呼び出されますが、しばらくするとメモリ不足エラーも発生します。

私の質問は、MapMaker を使用してキャッシュとして使用できるマップを作成する正しい方法は何ですか? weakKeys を使用すると、テスト プログラムができるだけ早くエントリを削除しないのはなぜですか? キャッシュ マップに参照キューを追加することはできますか?

4

3 に答える 3

8

多くのことが起こっている可能性がありますが、ソフト値を使用するテスト プログラムに関しては、まだガベージ コレクションされていない SoftReferences がある場合でも、OutOfMemoryError が発生する可能性があります。繰り返しますが、まだクリアされていない SoftReferences がある場合でも、OutOfMemoryError が発生する可能性があります。

SoftReferences は少し奇妙です。現在のメカニズムの説明については、http://jeremymanson.blogspot.com/2009/07/how-hotspot-decides-to-clear_07.htmlを参照してください。テスト ケースでは、GC に 2 つの完全な GC を実行する時間がありませんでした。

weakKeys を使用していた場合、CG はすぐにそれらをクリアし、完全な GC の一時停止を待つ必要はありませんでした。(b/c WeakReferences は積極的に収集されます。)

私の意見では、整数キーを使用したメモリに依存するキャッシュが必要な場合は、次の方法が適切だと思います。

data = new MapMaker().softValues().makeMap();

OutOfMemoryError をスローするテスト プログラムを簡単に作成できますが、実際のアプリケーションの動作が適切で、過度のプレッシャーを受けない場合は、問題ない可能性があります。SoftReferences を正しく設定するのはかなり困難です。

System.gc() を使用してメモリ不足を回避する必要がある場合は、代わりに、最大サイズが固定された LRU マップに切り替えることをお勧めします (例については、java.util.LinkedHashMap の javadoc を参照してください)。 、しかし、システムに完全な一時停止のガベージコレクションを何度も実行するように要求するよりも、最終的にはスループットが向上すると思います。

整数キーと weakKeys() に関する最後の注意: MapMaker は、弱いキーまたはソフト キーを使用するときにキーの同一性比較を使用しますが、これを正しく行うのはかなり困難です。次のことを目撃してください。

Map<Integer,String> map = new MapMaker().weakKeys().makeMap();
Integer a = new Integer(1);
Integer b = new Integer(1);
Integer c = 1; //auto box
Integer d = 1; //auto box
map.put(a, "A");
map.put(b, "B");
map.put(c,"C");
map.put(d,"D");
map.size() // size is 3;

幸運を。

于 2010-07-10T05:43:39.193 に答える
3

弱いキーは間違いのようです。強いキーは整数なので、使用してみてください。

于 2010-07-09T12:23:53.313 に答える
0

Suppliers.memoizeWithExpirationm、インスタント キャッシュに注目してください。

http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Suppliers.html#memoizeWithExpiration(com.google.common.base.Supplier、長い、java.util.concurrent. TimeUnit)

于 2010-08-09T12:39:00.010 に答える