Java软引用异常行为

Java软引用异常行为,java,garbage-collection,soft-references,Java,Garbage Collection,Soft References,if(cache.contains(“Username”)返回true,但 String userName=(String)cache.get(“userName”)返回null 这只会在很长一段时间后发生 如果在将该值添加到缓存几个小时后获得该值,则可以正确获得该值。 如果我在很长一段时间后得到这个值,比如说超过15-20小时,我会得到null 当GC清除SoftReference对象时,密钥是否仍保留在HashMap中?这就是这种行为的原因吗?是的,这是正常的行为 SoftReference

if(cache.contains(“Username”)
返回true,但

String userName=(String)cache.get(“userName”)
返回null

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

如果在将该值添加到缓存几个小时后获得该值,则可以正确获得该值。 如果我在很长一段时间后得到这个值,比如说超过15-20小时,我会得到null


当GC清除SoftReference对象时,密钥是否仍保留在HashMap中?这就是这种行为的原因吗?

是的,这是正常的行为

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

这与为其他类型的映射(例如映射)将某个键的值设置为null相同

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


是当GC清除SoftReference对象时,键保留在HashMap中。键和相应的值之间没有关系,除非它们在映射中。将
map的值
设为正常参考,除非map是GC,否则它们将始终位于map中

SoftReference
的referent被垃圾收集时,
SoftReference
被清除,即其referent字段被设置为
null

因此,键不仅保留在映射中,关联的值,
SoftReference
实例也保留在映射中,即使其referent字段为
null

但是,由于声明的
Map
字段和
cache.add(“Username”、“Tom”)
(String)cache.get(“Username”)
调用方之间必须有一个层,而您没有显示该层,因此该层甚至可能正确处理它

为了完整性,正确的实现可能如下所示

cache.add("Username", "Tom");
final Map cache=new ConcurrentHashMap();
/**删除所有清除的引用*/
私人空间清理(){
cache.values().removeIf(r->r.get()==null);
}
公共无效添加(E键,T值){
清洁();
cache.put(key,new SoftReference(value));
}
公共布尔包含(E键){
清洁();
return cache.computeIfPresent(key,(e,r)->r.get()==null?null:r)!=null;
}
公共T键(E键){
清洁();
对于(;;){
SoftReference ref=cache.computeIfPresent(key,(e,r)->r.get()==null?null:r);
如果(ref==null)返回null;
T值=ref.get();
if(value!=null)返回值;
}
}
此代码确保在查询时以原子方式删除收集的值的映射。此外,尽管不是必需的,
clean()
操作会删除地图的所有收集条目,以减少地图的空间(与
WeakHashMap
的内部工作方式相当)

但请注意,仍然无法保证当
cache.contains(“用户名”)
返回
true
时,后续的
cache.get(“用户名”)
将返回非
null
值。这个问题也称为“先检查后行动”反模式,它可能会失败,因为在检查和后续操作之间,可能会发生并发更新(在这里,即使只有一个线程在使用缓存,因为垃圾收集可能会异步进行),超过前面测试的结果


在这方面,
包含
操作对于大多数场景都是无用的。您必须调用
get
,以接收对值的强引用,如果非
null

则继续该引用,是
cache.get(“用户名”)
返回null,还是
cache.get(“用户名”).get()
返回null?前者(这是您的问题)与软引用无关,严格来说是一个HashMap问题。后者只是引用不再有效。如果您想努力从映射中删除条目,可以使用
ReferenceQueue
,但即使这样也不会自动发生。
cache.add("Username", "Tom");
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;
    }
}