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;
}
}