我前一段时间使用Soft References创建了一个缓存,但是在尝试解决一个bug时,我开始担心实际上我已经错误地完成了它并且当它不应该删除它时它会移除它。这就是我做到的:
private static final Map<String, SoftReference<Buffered>> imageMap =
new HashMap<String,SoftReference<Buffered>>();
public static synchronized Buffered addImage(String sum, final byte[] imageData)
{
SoftReference<Buffered> bufferedRef = imageMap.get(sum);
Buffered buffered;
if (bufferedRef!=null)
{
//There are no longer any hard refs but we need again so add back in
if(bufferedRef.get()==null)
{
buffered = new Buffered(imageData, sum);
imageMap.put(sum, new SoftReference(buffered));
}
else
{
buffered=bufferedRef.get();
}
}
else
{
buffered = new Buffered(imageData, logDescriptor, sum);
imageMap.put(sum, new SoftReference(buffered));
}
return buffered;
}
public static Buffered getImage(String sum)
{
SoftReference<Buffered> sr = imageMap.get(sum);
if(sr!=null)
{
return sr.get();
}
return null;
}
所以这个想法是一个调用进程可以添加新的Buffered对象,可以通过键和来识别/查找,然后只要这个Buffered对象被至少一个对象使用它就不会被删除。 map,但如果它不再被任何对象使用,那么如果内存变紧,它可能是垃圾收集。
但现在查看我的代码是重要的事情,关键字段总和总是被引用到其他地方(不一定是这种情况)
编辑:所以我尝试了Colin的解决方案,但我有点难过,因为putIfAbsent()似乎没有返回附加值。我修改了我的addImage方法以获得一些调试public static synchronized Buffered addImage(String sum, final byte[] imageData)
{
Buffered buffered = new Buffered(imageData, sum);
Buffered buffered2 = imageMap.get(sum );
Buffered buffered3 = imageMap.putIfAbsent(sum,buffered );
Buffered buffered4 = imageMap.get(sum );
System.out.println("Buffered AddImage1:"+buffered);
System.out.println("Buffered AddImage2:"+buffered2);
System.out.println("Buffered AddImage3:"+buffered3);
System.out.println("Buffered AddImage4:"+buffered4);
return buffered2;
}
返回
Buffered AddImage1:com.Buffered@6ef725a6
Buffered AddImage2:null
Buffered AddImage3:null
Buffered AddImage4:com.Buffered@6ef725a6
所以它清楚地表明Buffered实例不是从那里开始并且已成功构建和添加,但肯定应该由putIfAbsent返回?
答案 0 :(得分:2)
private static final ConcurrentMap<String, Buffered> imageMap =
new MapMaker().softValues().makeMap();
public static Buffered addImage(String sum, final byte[] imageData) {
Buffered buffered = new Buffered(imageData, sum);
Buffered inMap = imageMap.putIfAbsent(sum, buffered);
return inMap != null ? inMap : buffered;
}
public static Buffered getImage(String sum) {
return imageMap.get(sum);
}
由于这是ConcurrentMap
并使用putIfAbsent
,因此您无需同步addImage
,除非创建Buffered
的实例非常昂贵。当你的值被垃圾收集时,这也会处理从地图中删除的条目,这与你的代码不同。
修改:如果您调用getImage
并获取null
(可能是因为该值是垃圾回收),您会怎么做?是否有某种方法可以根据byte[]
键获取图像数据sum
?如果是这样,您可能希望将为Buffered
创建sum
实例的过程封装为Function<String, Buffered>
。这允许您使用计算地图而不是正常地图:
private static final ConcurrentMap<String, Buffered> imageMap = new MapMaker()
.softValues()
.createComputingMap(getBufferedForSumFunction());
通过这种方式,您甚至可能不需要addImage
方法...如果在地图上调用了get
且它没有给定sum
的条目,它将调用该函数,缓存结果并将其返回。
答案 1 :(得分:1)
如果您想要做的只是允许数据在任何地方都没有被引用时收集垃圾,请使用WeakHashMap。
如果您希望地图实际上能够重新创建数据(如果数据不再可用),那么您需要修改getImage()以检查引用是否可用,如果没有,则重新创建它。
在我看来,你想要的是前者。
软引用和弱引用之间的区别在于垃圾收集器使用算法来决定是否回收软可访问对象,但总是回收弱可达对象。 (ref)