Javaの弱参照をキャッシュに使いましょう、という話がよくある。しかしこの手を考えなしに使うと、罠にはまる。私がはまった。
キャッシュ対象を生成する操作で、大量のメモリを確保・解放したら、なにが起こるか? あるときはなにも起こらない。あるときはガベージコレクションが起こる。起動後しばらくは前者であることが多い。起動からしばらくすると、後者がぼちぼちと起こるようになる。
ガベージコレクションは弱参照を消してしまう。
ということは、だ――キャッシュ対象Aを生成するときにB, C, D...が消されてしまい、その消されたBを生成するときにA, C, D...が消されてしまい…… という現象が起こる。しかもこの現象は、起動直後には起こらず、しばらく経ってから起こるようになる。
いったんこの現象が起こると、まるでスイッチが入ったように止まらなくなる。特に、複数スレッドでキャッシュ対象を生成している場合には顕著だ。キャッシュが効いているあいだには、めったにキャッシュ対象生成は起こらない。しかし、いったんガベージコレクションが起きたが最後、複数スレッドで同時にキャッシュ対象を生成しようとするため、いっそうガベージコレクションを起こしやすくなってしまう。
対策は、弱参照ではなくLRUMapを使うこと。