这是使用软引用的正确方法吗

发布于 2024-10-03 04:57:34 字数 2363 浏览 6 评论 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;
}

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

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

编辑:所以我尝试了科林的解决方案,但我有点困惑,因为 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 返回?

I created a cache using Soft References a while ago, but in trying to resolve a bug I'm getting concerned that actually I've done it incorrectly and it's removing objects when it shouldn't. This is how I've done it:

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

So the idea is a calling process can add new Buffered objects which can be identifed/looked up by the key sum, then as long as this Buffered object is being used by at least one object it won't be removed from the map, but if it is no longer being used by any objects then it could be garbage collection if memory gets tight.

But looking at my code now is the important thing that the key field sum is always being referenced somewhere else (which isn't necessarily the case)

EDIT: So I tried Colin's solution but I'm kind of stumped because putIfAbsent() doesn't seem to return the added value. I modified my addImage method to get some debugging

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

returns

Buffered AddImage1:com.Buffered@6ef725a6
Buffered AddImage2:null
Buffered AddImage3:null
Buffered AddImage4:com.Buffered@6ef725a6

So it clearly show the Buffered instance wasn't there to start with and is successfully constructed and added, but surely should be returned by putIfAbsent?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

彻夜缠绵 2024-10-10 04:57:34

我建议使用 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);
}

由于这是一个 ConcurrentMap 并使用 putIfAbsent,因此您不必同步 addImage,除非创建 Buffered 实例> 很贵。与您的代码不同,这还可以在条目的值被垃圾收集时实际从映射中删除条目。

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

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

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

I'd recommend using Guava's MapMaker instead of doing this yourself.

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

Since this is a ConcurrentMap and uses putIfAbsent, you don't have to synchronize addImage unless creating an instance of Buffered is expensive. This also handles actually removing entries from the map when their value is garbage collected, unlike your code.

Edit: What do you do if you call getImage and get null as a result (perhaps because the value was garbage collected)? Is there some way that you can get the image data byte[] based on the sum key? If so, you may want to encapsulate the process of creating an instance of Buffered for a given sum as a Function<String, Buffered>. This allows you to use a computing map instead of a normal one:

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

Done this way, you may not even need an addImage method... if get is called on the map and it doesn't have an entry for the given sum, it'll call the function, cache the result and return it.

浮生未歇 2024-10-10 04:57:34

如果您只想允许数据在未在任何地方引用时进行垃圾收集,请使用 Wea​​kHashMap。

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

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

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

If all you want to do is allow data to get garbage collected when it's not referenced anywhere, use a WeakHashMap.

If you want your map to actually be able to recreate the data if it is no longer available, then you'll need to modify the getImage() to check if the reference is available and if not, recreate it.

It seems to me that what you want is the former.

The difference between a soft reference and a weak reference is that the garbage collector uses algorithms to decide whether or not to reclaim a softly reachable object but always reclaims a weakly reachable object. (ref)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文