[英]Java SoftReference strange behaviour
Map<E, SoftReference<T>> cache = new ConcurrentHashMap<E, SoftReference<T>>();
我有地圖聲明了一個像我上面用作Cache的地圖。
問題是我將項目添加到緩存后立即執行緩存上的所有操作,但不是稍后。
例如:
cache.add("Username", "Tom");
if(cache.contains("Username"))
返回true但是
String userName = (String)cache.get("Username")
返回null。
這種情況只在很長一段時間后發生。
如果我在將它添加到緩存幾個小時后得到該值,我會正確獲得該值。 如果我在很長一段時間后得到這個值,比如超過15-20小時,我就會得到null。
當GC清除SoftReference對象時,密鑰是否仍保留在HashMap中? 這是出現這種行為的原因嗎?
當SoftReference
被垃圾收集時, SoftReference
被清除 ,即它的引用字段被設置為null
。
因此,不僅密鑰保留在地圖中,相關的值SoftReference
實例也會保留在地圖中,即使其引用字段為null
。
但是因為在你聲明的Map<E, SoftReference<T>>
字段和cache.add("Username", "Tom")
和(String)cache.get("Username")
的調用者之間必須有一個層,你沒有展示,甚至可能是這個圖層正確處理它的情況。
為了完整起見,正確的實現可能看起來像
final Map<E, SoftReference<T>> cache = new ConcurrentHashMap<>();
/** remove all cleared references */
private void clean() {
cache.values().removeIf(r -> r.get() == null);
}
public void add(E key, T value) {
clean();
cache.put(key, new SoftReference<>(value));
}
public boolean contains(E key) {
clean();
return cache.computeIfPresent(key, (e,r) -> r.get()==null? null: r) != null;
}
public T get(E key) {
clean();
for(;;) {
SoftReference<T> ref = cache.computeIfPresent(key, (e,r) -> r.get()==null? null: r);
if(ref == null) return null;
T value = ref.get();
if(value != null) return value;
}
}
此代碼確保在查詢時以原子方式刪除收集值的映射。 此外,雖然不是必需的,但clean()
操作會刪除所有收集的地圖條目,以減少地圖的空間(與WeakHashMap
內部工作方式相當)。
但請注意,仍然沒有保證當cache.contains("Username")
返回true
,后續的cache.get("Username")
將返回非null
值。 這個問題也稱為check-then-act反模式,它可能會失敗,因為在檢查和后續操作之間,可能會發生並發更新(這里即使你只使用一個線程的緩存,垃圾收集可能是異步發生的),過時的測試結果已經過時了。
在這方面,對於大多數情況, contains
操作是無用的。 您必須調用get
,以接收對值的強引用,並繼續該引用(如果為非null
。
按照oracle文檔
在虛擬機拋出OutOfMemoryError之前,保證已清除對軟可訪問對象的所有軟引用。
是當GC清除SoftReference對象時,該鍵保留在HashMap中。 除了它們在地圖內部之外,鍵和相應的值沒有任何關系。 將map's value
設為普通參考map's value
,除非地圖為GC,否則它們將始終位於地圖中。
是的,這是正常的行為。
SoftReference被垃圾收集,導致Map中的值被設置為null 。
對於其他類型的地圖(例如,地圖),將某個鍵的值設置為null是相同的
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.