10

我不习惯处理 Java 中的软引用和弱引用,但我理解这个原理,因为我习惯于处理像 Gemfire 这样的数据网格,当内存已满时,它会为 hdd 功能提供溢出,可能使用软引用或类似的东西猜测。

我在番石榴中不明白的是,它提供了使键变软/弱的方法,以及使值变软/弱的方法。

我只是想知道创建具有非软值的软键有什么意义?我的意思是,当开始收集软引用时,我们无法再通过它的键找到条目,那么为什么我们希望这些值保留在映射中呢?

有人可以给我们一些用例:

  • 弱键/软值
  • 弱键/正​​常值
  • 软键/弱值
  • 软键/正常值

谢谢


编辑 我不确定我的问题是否足够精确,所以我想知道的是:

  • 收集密钥时(弱/软),值会发生什么(非弱/软)
  • 当一个值被收集(弱/软)时,键会发生什么
  • 缺少部分(键或值)的条目是否保留在缓存中?
  • 当您希望此类条目保留在缓存中时,是否有任何用例。

编辑: 正如 Kevin Bourillon 的回答所讨论的,最后我想我明白为什么使用软键没有任何意义。原因如下:

static class KeyHolder {
    final private String key;
    public KeyHolder(String key) {
        this.key = key;
    }
    public String getKey() {
        return key;
    }
    @Override
    public boolean equals(Object o) {
        KeyHolder that = (KeyHolder)o;
        boolean equality = this.getKey().equals(that.getKey());
        return equality;
    }
    @Override
    public int hashCode() {
        return key != null ? key.hashCode() : 0;
    }
    @Override
    public String toString() {
        return "KeyHolder{" +
                "key='" + key + '\'' +
                '}';
    }
}

public static void main(String[] args) {
    System.out.println("TESTING WEAK KEYS");
    testMap( new MapMaker().weakKeys().<KeyHolder,String>makeMap() );


    System.out.println("\n\n");
    System.out.println("TESTING SOFT KEYS");
    testMap(new MapMaker().softKeys().<KeyHolder, String>makeMap());


    System.out.println("\n\n");
    System.out.println("TESTING SOFT REFERENCES");
    KeyHolder key1 = new KeyHolder("toto");
    KeyHolder key2 = new KeyHolder("toto");
    SoftReference<KeyHolder> softRef1 = new SoftReference<KeyHolder>(key1);
    SoftReference<KeyHolder> softRef2 = new SoftReference<KeyHolder>(key2);
    System.out.println( "equals keys? " + key1.equals(key2) );
    System.out.println( "equals ref? " + softRef1.equals(softRef2) );
}

private static void testMap(Map<KeyHolder,String> map) {
    KeyHolder strongRefKey = new KeyHolder("toto");
    KeyHolder noStrongRefKey = new KeyHolder("tata");
    map.put(strongRefKey,"strongRef");
    map.put(noStrongRefKey,"noStrongRefKey");
    // we replace the strong reference by another key instance which is equals
    // this could happen for exemple in case of serialization/deserialization of the key
    noStrongRefKey = new KeyHolder("tata");
    System.gc();
    System.out.println( "strongRefKey = " + map.get(strongRefKey) );
    System.out.println( "noStrongRefKey = " + map.get(noStrongRefKey) );
    System.out.println( "keyset = " + map.keySet() );
}

此代码产生输出:

TESTING WEAK KEYS
strongRefKey = strongRef
noStrongRefKey = null
keyset = [KeyHolder{key='toto'}]



TESTING SOFT KEYS
strongRefKey = strongRef
noStrongRefKey = null
keyset = [KeyHolder{key='tata'}, KeyHolder{key='toto'}]



TESTING SOFT REFERENCES
toto == toto -> true
equals keys? true
equals ref? false

如您所见,对于(已弃用)软键映射,包含“tata”的 KeyHolder 仍然存在于映射中。但是请注意,我仍然无法使用新创建的键“ new KeyHolder("tata");” 找到我的条目这是因为,我的键有意义地等于,但它们周围的引用包装器不等于,因为它们的等于方法在 Guava 中没有被覆盖! 在这种情况下,是的,softKeys 没有任何意义,因为您绝对需要保留对该密钥的身份引用才能检索它。

4

2 回答 2

13

softKeys从来没有意义,所以我们删除了该方法。softValues是软引用有意义的唯一方法,假设值实例也不能以缓存之外的其他方式访问。

然后使用weakKeys基本上归结为您是否想要密钥的身份平等。如果键覆盖equals并且您需要相等行为,则不能使用它。如果您想要身份,那么weakKeys您就是如何获得身份的,这也是有道理的,因为一旦对密钥的所有其他引用都已被 GC 处理,则无论如何都无法查找该条目,因此不妨将其删除.

I actually am not entirely clear on when weakValues is useful, and was going to look into it. It would probably be a case where weakKeys is not an option (say, Integer keys), and where the values are ordinarily strongly referenced through other means, like some sort of session object, but when that object goes away, it signifies that no one will be looking for this in the cache anymore. It seems slightly farfetched when I put it that way, though.

于 2012-06-11T14:55:41.237 回答
2

非 Guava WeakHashMap 基于弱键并在不再使用键时丢弃整个条目。因此,没有“留在地图中的价值”。这是我从这种数据结构中直观地期望的那种行为,我强烈怀疑 Guava 的行为方式相同,对于收集的软引用也是如此。

请注意,Java Collections 库仅提供弱键/正常值映射,而不是弱键/软值或弱键/弱值,因为用户可以自己包装值(但不能像在不应该期望哈希图对实际键和对键的弱引用以相同的方式工作)。

我自己曾经使用过 WeakHashMap 将可能被其他地方的机制过时的对象与与该机制无关的信息关联起来(准确地说:可能被某些碰撞破坏的移动球与其最近运动的信息)。这些值是专门为该映射创建的,而不是在其他地方引用(除了在本地方法中,只能在它们的键仍然存在时调用)。因此,除了对值的正常引用之外,没有必要使用任何东西,因为当键过时时,该值将变为未引用且可被垃圾回收。

于 2012-06-11T09:33:41.103 回答