java - 对于具有弱 HashMap 的重复数据删除器,正确的实现是什么?

标签 java garbage-collection weak-references

我在 Aleksey Shipilev 的幻灯片“字符串教义问答”(https://shipilev.net/talks/joker-Oct2014-string-catechism.pdf,幻灯片 49 ff)中发现了“重复数据删除器”的概念。许多 Java 程序员都知道 String.intern() 中类似的实习概念。
但是,如果不使用弱引用,重复数据删除器可能会导致内存泄漏。 我想知 Prop 有弱 HashMap 的重复数据删除器的正确实现应该是什么样子。我倾向于选项 B,但我不确定。

选项A: 使用 Wea​​kHashMap 就足够了。 “弱键”确保对象在不再使用时被删除。

示例实现:

public class SimpleWeakHashMapDeduplicator {
    private final WeakHashMap<Object, Object> weakHashMap = new WeakHashMap<>();

    public Object deduplicate(Object potentialDuplicate) {
        if(potentialDuplicate == null) {
            return null;
        } else {
            return weakHashMap.computeIfAbsent(potentialDuplicate, (key)->key);
        }
    }
}

选项B: 使用 Wea​​kHashMap 是不够的。所有值都必须是 WeakReferences,因为 ComplicatedWeakHashMapDeduplicator 的实例强烈引用弱 HashMap ,该映射强烈引用带有条目的数组,其中一个条目强烈引用该值。只有键被映射弱引用。我哪里错了?

示例实现:

public class ComplicatedWeakHashMapDeduplicator {
    private final WeakHashMap<Object, WeakReference<Object>> weakHashMap = new WeakHashMap<>();

    public Object deduplicate(Object potentialDuplicate) {
        if(potentialDuplicate == null) {
            return null;
        } else {
            return weakHashMap.computeIfAbsent(potentialDuplicate, WeakReference::new).get();
        }
    }
}

你觉得怎么样?

最佳答案

“选项 B”的方向是正确的,但还没有完全实现。此行有问题:

return weakHashMap.computeIfAbsent(potentialDuplicate, WeakReference::new).get();

让我们假设弱映射包含先前缓存的值。您调用computeIfAbsent并获取引用。在您 get() 之前的短暂窗口期间,没有什么可以阻止垃圾收集器回收其所指对象。如果发生这种情况,您最终将返回 null

你的逻辑需要更加稳健一些。尝试这样的事情:

public final class WeakCache<T> {
    private final WeakHashMap<T, WeakReference<T>> _map = new WeakHashMap<>();

    public synchronized T cache(final T value) {
        if (value == null) {
            return null;
        }

        final WeakReference<T> oldReference = _map.get(value);

        if (oldReference != null) {
            final T oldValue = oldReference.get();

            if (oldValue != null) {
                return oldValue;
            }
        }

        _map.put(value, new WeakReference<>(value));

        return value;
    }
}

这将防止您的缓存值泄漏,但值得询问的是您在释放旧值时有多渴望。如果你的值(value)观往往是短暂的,但预计会一次又一次地出现,你可能想坚持更长时间。在这种情况下,您可以考虑使用 SoftReference 作为值包装器。软引用的行为类似,但它们倾向于保留其引用对象,直到面临内存压力。 Oracle 的“服务器”VM(x64 的默认值)更愿意扩展堆而不是释放软引用,因此应用程序的内存使用量可能会更快达到极限,此时它将开始驱逐无法访问的值。这是一种权衡,也不是“一刀切”的解决方案。灵活的实现可能会将引用创建抽象为可插入策略,从而在首次创建缓存时在弱引用和软引用之间进行选择变得很简单。

关于java - 对于具有弱 HashMap 的重复数据删除器,正确的实现是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46183578/

相关文章:

Java Finalizer 方法和 GC

java - 为什么javac在同一个类中找不到符号?

java - java中的垃圾收集器-将对象设置为null

java - Spring 原型(prototype) Bean 和 Spring 的好处

actionscript-3 - 如何在 Action Script 3.0 中回收(垃圾回收) "static const Object"?

java - 弱引用与设置为 Null

ios - MVVM - 使用闭包将 ViewModel 与数据源绑定(bind) : capture list needed?

java - 文件未保存

java - 创建一个 "Command"控制台

java - 找到从 A 到 Z 的所有路径的有效算法?