1
Map<E, SoftReference<T>> cache = new ConcurrentHashMap<E, SoftReference<T>>();

我已经 map 声明了一个类似于上面的地图,我将其用作缓存。

问题是我要在将项目添加到缓存后立即在缓存上执行所有操作,而不是稍后。

例如:

cache.add("Username", "Tom");

if(cache.contains("Username"))返回 true 但

String userName = (String)cache.get("Username")返回空值。

这只有在很长一段时间后才会发生。

如果我在将它添加到缓存几个小时后得到了这个值,我就得到了正确的值。如果我在很长一段时间后得到这个值,比如超过 15-20 小时,我会得到空值。

GC清除SoftReference对象时,key会留在HashMap中吗?这是这种行为的原因吗?

4

3 回答 3

2

当 a 的引用SoftReference被垃圾回收时,SoftReference清除,即它的引用字段被设置为null

因此,不仅 key 保留在 map 中,关联的 valueSoftReference实例也保留在 map 中,即使它的引用字段是null.

Map<E, SoftReference<T>>但是,由于在您声明的字段和 and 的调用者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

于 2019-01-07T14:48:38.163 回答
1

根据oracle 文档

在虚拟机抛出 OutOfMemoryError 之前,保证已清除对软可访问对象的所有软引用。

Yes 当 GC 清除 SoftReference 对象时,key 保留在 HashMap 中。键和对应的值除了在地图内时没有任何关系。做map's value一个正常的参考,除非地图是 GC,否则它们将始终在地图中。

于 2018-12-27T14:49:30.440 回答
0

是的,这是正常行为。

SoftReference 被垃圾收集,导致 Map 中的值设置为null

与其他类型的map(如Map)将某个key的值设置为null是一样的

于 2018-12-27T14:48:12.750 回答