回收緩存方案
Guava Cache提供了三種基本的緩存回收方式:
- 基于容量回收
- 定時回收
- 基于引用回收
1、基于容量的回收(size-based eviction)
- 場景:如果要規(guī)定緩存項的數(shù)目不超過固定值
- 用法:CacheBuilder.maximumSize(long)
緩存將嘗試回收最近沒有使用或總體上很少使用的緩存項微酬《端——警告:在緩存項的數(shù)目達到限定值之前薇芝,緩存就可能進行回收操作——通常來說,這種情況發(fā)生在緩存項的數(shù)目逼近限定值時。
另外,不同的緩存項有不同的“權重”(weights)——例如泉哈,如果你的緩存值,占據(jù)完全不同的內存空間破讨,你可以使用CacheBuilder.weigher(Weigher)指定一個權重函數(shù)丛晦,并且用CacheBuilder.maximumWeight(long)指定最大總重。在權重限定場景中提陶,除了要注意回收也是在重量逼近限定值時就進行了烫沙,還要知道重量是在緩存創(chuàng)建時計算的,因此要考慮重量計算的復雜度隙笆。
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumWeight(100000)
.weigher(new Weigher<Key, Graph>() {
public int weigh(Key k, Graph g) {
return g.vertices().size();
}
})
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) { // no checked exception
return createExpensiveGraph(key);
}
});
2锌蓄、定時回收(Timed Eviction)
CacheBuilder
提供兩種定時回收的方法:
- expireAfterAccess(long, TimeUnit):緩存項在給定時間內沒有被讀/寫訪問,則回收撑柔。請注意這種緩存的回收順序和基于大小回收一樣瘸爽。
- expireAfterWrite(long, TimeUnit):緩存項在給定時間內沒有被寫訪問(創(chuàng)建或覆蓋),則回收铅忿。如果認為緩存數(shù)據(jù)總是在固定時候后變得陳舊不可用剪决,這種回收方式是可取的。
如下文所討論檀训,定時回收周期性地在寫操作中執(zhí)行柑潦,偶爾在讀操作中執(zhí)行。
3峻凫、基于引用的回收(Reference-based Eviction)
通過使用弱引用的鍵
妒茬、或弱引用的值
、或軟引用的值
蔚晨,Guava Cache可以把緩存設置為允許垃圾回收乍钻。關于軟引用個弱引用的概念可以參考強引用肛循、弱引用、軟引用银择、虛引用
- CacheBuilder.weakKeys():使用弱引用存儲鍵多糠。當鍵沒有其它(強或軟)引用時,緩存項可以被垃圾回收浩考。因為垃圾回收僅依賴恒等式(==)夹孔,使用弱引用鍵的緩存用==而不是equals比較鍵。
- CacheBuilder.weakValues():使用弱引用存儲值析孽。當值沒有其它(強或軟)引用時搭伤,緩存項可以被垃圾回收。因為垃圾回收僅依賴恒等式(==)袜瞬,使用弱引用值的緩存用==而不是equals比較值怜俐。
-
CacheBuilder.softValues():使用軟引用存儲值。軟引用只有在響應內存需要時邓尤,才按照全局最近最少使用的順序回收拍鲤。考慮到使用軟引用的性能影響汞扎,我們通常建議使用更有性能預測性的緩存大小限定(見上文季稳,基于
容量回收
)。使用軟引用值的緩存同樣用==而不是equals比較值澈魄。
3景鼠、顯式清除
任何時候,你都可以顯式地清除緩存項痹扇,而不是等到它被回收:
- 個別清除:Cache.invalidate(key)
- 批量清除:Cache.invalidateAll(keys)
- 清除所有緩存項:Cache.invalidateAll()
清理什么時候發(fā)生铛漓?
- 使用CacheBuilder構建的緩存不會"自動"執(zhí)行清理和回收工作,也不會在某個緩存項過期后馬上清理帘营,也沒有諸如此類的清理機制票渠。相反,它會在寫操作時順帶做少量的維護工作芬迄,或者偶爾在讀操作時做——如果寫操作實在太少的話问顷。
- 這樣做的原因在于:如果要自動地持續(xù)清理緩存,就必須有一個線程禀梳,這個線程會和用戶操作競爭共享鎖杜窄。此外,某些環(huán)境下線程創(chuàng)建可能受限制算途,這樣CacheBuilder就不可用了塞耕。
- 相反,我們把選擇權交到你手里嘴瓤。如果你的緩存是高吞吐的扫外,那就無需擔心緩存的維護和清理等工作莉钙。如果你的 緩存只會偶爾有寫操作,而你又不想清理工作阻礙了讀操作筛谚,那么可以創(chuàng)建自己的維護線程磁玉,以固定的時間間隔調用Cache.cleanUp()。ScheduledExecutorService可以幫助你很好地實現(xiàn)這樣的定時調度驾讲。
刷新
- 刷新和回收不太一樣蚊伞。正如LoadingCache.refresh(K)所聲明,刷新表示為鍵加載新值吮铭,這個過程可以是異步的时迫。在刷新操作進行時,緩存仍然可以向其他線程返回舊值.
- 而不像回收操作谓晌,讀緩存的線程必須等待新值加載完成掠拳。
- 如果刷新過程拋出異常,緩存將保留舊值扎谎,而異常會在記錄到日志后被丟棄[swallowed]碳想。