2

最大保持容量が N のキャッシュが必要です。それ以外の場合は GC の対象となる最大 N 個のオブジェクトを保持できるようにしています。ここで、アプリケーション自体が、以前にキャッシュに追加されたオブジェクトへの N+1 の強力な参照を現在保持している場合、キャッシュにも N+1 を保持する必要があります。なんで?キャッシュは、この N+1 番目のオブジェクトがそうでない場合よりも長く収集されるのを妨げないため、より大きなハッシュ テーブルをより多くのキャッシュ ヒットと交換しても問題ありません。

別の言い方をすれば、追加されたすべてのオブジェクトを強力な到達可能性を維持しながら保持し、サイズ == N を維持するのに十分な非強力な到達可能オブジェクトも保持するオブジェクト キャッシュが必要です。

N=100 で作成されたキャッシュがあります。サイズは 0 から始まります。150 個のオブジェクトが追加され、サイズは 150 になります。これらのオブジェクトの 100 個は、強く到達できなくなります (弱く、柔らかく、何でも)。キャッシュはそれらのうち 50 個を削除し、50 個を保持します。サイズは 100 です。さらに 49 個の強力に到達可能なオブジェクトが追加されます。サイズは 100 のままですが、そのうちの 99 個が強く到達可能であり、1 つだけが強く到達可能ではありません。何が起こったのかというと、49 の古い、強く到達できないオブジェクトが新しい 49 に置き換えられたのです。新しいオブジェクトは強く到達可能だからです。

動機

実際には、多くのユースケースで直感的に必要なことだと思います。通常、キャッシュの容量は、キャッシュ ヒット確率と引き換えに最大メモリ使用量を保証します。キャッシュが保持するオブジェクトの到達可能性を認識していれば、キャッシュは、最大メモリ使用量の保証を変更することなく、より高いキャッシュ ヒット確率を提供できます。

トラブル

JVMではできないのではないかと心配しています。私は別のことを言われることを望んでいますが、事実が不可能であることがわかっている場合は、根拠があればその答えも受け入れます.

4

3 に答える 3

2

LRU または FIFO キャッシュとして構成された LinkedHashMap にエントリを追加できます。WeakHashMap も使用できます。両方のマップにキーを追加すると、WHM にあるにもかかわらず、LHM によってクリーンアップが妨げられます。LHM がキーを破棄すると、WHM にある場合とない場合があります。

例えば

private final int retainedSize;
private final Map<K,V> lruMap = new LinkedHashMap<K, V>(16, 0.7f, true) {
    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > retainedSize;
    }
};
private final Map<K,V> weakMap = new WeakHashMap<K, V>();

public void put(K k, V v) {
    lruMap.put(k, v);
    weakMap.put(k,v);
}

public V get(K k) {
    V v = lruMap.get(k);
    return v == null ? weakMap.get(k) : v;
}

これを行う理由の 1 つは、WeakHashMap が一度に明確になるため、ヒット率が非常に急激に低下する可能性があるためです。このアプローチにより、フル GC にヒットした後、追いつくためにパフォーマンスが大幅に低下することはありません。;)

于 2012-09-08T07:11:03.093 に答える
0

あなたが望むことは理にかなっていると思いますが、それほど多くはないかもしれません。値が非常に大きい (数キロバイト) と仮定しましょう。そうしないと、他の場所で強く保持されている値のキャッシュも高価になる可能性があります。このオーバーヘッドは無視してください。実際、キャッシュのメモリ コストは一定です。ただし、この目標を追求する価値があるかどうかはわかりません-プログラム全体でほぼ一定量のメモリを使用する方法に興味があります(未使用のメモリをあまり残したくありません。交換を開始します)。

アイデア: キャッシュは、登録された弱 (またはソフト) 参照を使用する必要があります。1別のスレッドを使用してループ内で呼び出しReferenceQueue.remove()いくつかの条件をチェックします。それに応じて、キャッシュから対応するエントリを削除するか (Guava のように)、値を復活させて一時的にガベージ コレクションから保護します。3 . これは機能するはずですが、GC の実行ごとに時間がかかります。reference.get()


1オーバーライドfinalize()も同様です。実際、reference.get()エンキューすると常に null が返されるため、復活には使用できないため、これが唯一の方法のようです。

2条件は、「GC 実行ごとに 100 回実行する」のようなものにする必要があります。

3 GC が実際にこのように機能するかどうかはわかりませんが、機能していると思います。そうでない場合は、代わりに値のコピーを使用できます。また、次回値が強力な到達可能性を失ったときにどうなるかはわかりませんが、これも確実に解決できます (たとえば、新しい参照を作成します)。

于 2012-09-08T02:11:29.503 に答える
0

チェックアウトしてくださいWeakHashMap。古い参照は自動的に削除されます。配置する前に、サイズがしきい値を超えているかどうかを確認し、新しい値の入力をスキップできます。

サイズがしきい値を超えている場合は、値を上書きして破棄することもできます。

サイズがしきい値よりも大きい場合は、キャッシュ削除ポリシーが必要ないため、新しい要素の挿入をスキップするだけでよいため、この方法は提案どおりに機能します。

于 2012-09-08T00:21:31.073 に答える