0

前段时间我使用软引用创建了一个缓存,但在尝试解决一个错误时,我开始担心实际上我做错了,它在不应该删除对象时删除了对象。这就是我的做法:

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

所以这个想法是一个调用进程可以添加新的缓冲对象,这些对象可以通过键和来识别/查找,然后只要这个缓冲对象被至少一个对象使用,它就不会从地图中删除,但是如果它不再被任何对象使用,那么如果内存变得紧张,它可能是垃圾收集。

但是现在看我的代码很重要,关键字段总和总是在其他地方被引用(不一定是这种情况)

编辑:所以我尝试了 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 返回?

4

2 回答 2

2

我建议使用GuavaMapMaker而不是自己这样做。

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

由于这是 a ConcurrentMapand uses putIfAbsent,因此您不必同步addImage,除非创建 的实例Buffered很昂贵。与您的代码不同,当它们的值被垃圾收集时,这也处理从映射中实际删除的条目。

编辑:getImage如果您调用并得到结果,您会怎么做null(可能是因为该值是垃圾收集的)?有什么方法可以byte[]根据sum密钥获取图像数据?如果是这样,您可能希望将创建Buffered给定实例的过程封装sumFunction<String, Buffered>. 这允许您使用计算地图而不是普通地图:

private static final ConcurrentMap<String, Buffered> imageMap = new MapMaker()
    .softValues()
    .createComputingMap(getBufferedForSumFunction());

这样做,您甚至可能不需要addImage方法......如果get在地图上调用并且它没有给定的条目sum,它将调用函数,缓存结果并返回它。

于 2010-11-17T21:19:39.687 回答
1

如果您只想让数据在没有被任何地方引用时被垃圾收集,请使用 Wea​​kHashMap。

如果您希望地图实际上能够在数据不再可用时重新创建数据,那么您需要修改 getImage() 以检查参考是否可用,如果不可用,请重新创建它。

在我看来,你想要的是前者。

软引用和弱引用的区别在于垃圾收集器使用算法来决定是否回收一个软可达对象,但总是回收一个弱可达对象。(参考)

于 2010-11-17T21:04:10.820 回答