【问题标题】:Guava CacheBuilder or MapMaker with Weak/Soft usage使用弱/软的 Guava CacheBuilder 或 MapMaker
【发布时间】:2012-06-11 09:18:38
【问题描述】:

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

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

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

谁能给我们一些用例:

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

谢谢


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

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

编辑: 正如 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");”找到我的条目 这是因为,我的键是有意义的,但它们周围的引用包装器不等于,因为它们的 equals 方法在 Guava 中没有被覆盖! 在这种情况下,是的,softKeys 没有任何意义,因为您绝对需要保留对该密钥的身份引用才能检索它。

【问题讨论】:

    标签: java caching guava


    【解决方案1】:

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

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

    我实际上并不完全清楚 weakValues 何时有用,并打算研究它。这可能是weakKeys 不是一个选项(例如,Integer 键)的情况,并且这些值通常通过其他方式强烈引用,例如某种会话对象,但是当 那个 em> 对象消失,这意味着没有人会再在缓存中寻找它。不过,当我这样说时,它似乎有点牵强。

    【讨论】:

    • 所以如果你使用软值并且大部分值都被收集了,你仍然有一个缓存,里面有很多条目,很多键,但根本没有值?
    • 因此,如果您使用弱键并且在缓存之外不再有键引用,则键会被收集,但值会怎样?它们是否保存在缓存中?
    • 当你说软键没有意义时,我不确定。如果您有重建新密钥的方法怎么办:您可能希望将密钥保存在缓存中,即使您没有任何身份参考,因为您可以再次创建适当的密钥否?被克隆/序列化/反序列化的密钥怎么样。你明白我的意思吗
    • softKeys 似乎可以工作的任何情况下,weakKeys 工作得更好。一旦密钥不再强可达,weakKeys 允许它被 GC'd。 softKeys 可能会任意持有更长的时间——但目的是什么?没有人可以查找该条目,因为如前所述,密钥不再是强可达的!所以我们会遇到一些极端情况,比如“哦,但是我对密钥的所有 other 引用也绝对是软的”,或者“哦,但我有时会遍历 asMap() 条目并恢复某些出于某种原因钥匙”......这些情况实际上从未出现过。
    • @KevinBourrillion,请检查我的编辑。正如我所说,一个密钥可以被序列化,例如反序列化。如果 Guava 覆盖 SoftReference 的 equals 方法,我认为软键是有意义的。我已经看到 Guava 使用“等价机制”,我认为,对于软引用,defaultEquivalence 不应该是身份,而是等于,来处理这种情况。然后,使用 softKeys 将是有意义的,因为您将能够通过键的副本检索映射值。你怎么看?
    【解决方案2】:

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

    请注意,Java Collections 库仅提供弱键/普通值映射,而不是弱键/软值或弱键/弱值,因为用户可以自己包装值(但不能对键进行类似操作)对于实际键和对键的弱引用,不应期望哈希图中的查找以相同的方式工作。

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

    【讨论】:

    • 请注意,使用弱键时有一个警告:“警告:当使用此方法 [weakKeys()] 时,生成的缓存将使用标识 (==) 比较来确定键的相等性。 "
    猜你喜欢
    • 1970-01-01
    • 2011-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-04
    相关资源
    最近更新 更多