Java 是否需要一个HashMap<;字符串,WeakReference<&燃气轮机&燃气轮机;如果未明确清除,是否可能导致内存泄漏?

Java 是否需要一个HashMap<;字符串,WeakReference<&燃气轮机&燃气轮机;如果未明确清除,是否可能导致内存泄漏?,java,memory-leaks,garbage-collection,weak-references,Java,Memory Leaks,Garbage Collection,Weak References,我遇到过一段代码,我确信这段代码会导致意外内存泄漏: Object user = getUser("Bob"); Map<String, WeakReference<Object>> map = new HashMap<>(); map.put("Bob", new WeakReference( user ) ); 因此,除非有一个清理例程用空值刷新所有键,否则我的映射将继续增长 那么问题就变成了如何解决这个问题?是否有一个映射构造,我可以使用它在值被销毁

我遇到过一段代码,我确信这段代码会导致意外内存泄漏:

Object user = getUser("Bob");

Map<String, WeakReference<Object>> map = new HashMap<>();
map.put("Bob", new WeakReference( user ) );
因此,除非有一个清理例程用空值刷新所有键,否则我的映射将继续增长

那么问题就变成了如何解决这个问题?是否有一个映射构造,我可以使用它在值被销毁时自动刷新我的条目

我考虑做一些类似的事情:

Object user = getUser("Bob");

Map<String, WeakReference<Object>> map = new WeakHashMap<>();
map.put(user.getUsername(), new WeakReference( user ) );
objectuser=getUser(“Bob”);
Map Map=newweakhashmap();
map.put(user.getUsername(),new WeakReference(user));
但这似乎是一个非常有限的用例,其中我的密钥必须是从我的值中检索到的对象。使用
WeakHashMap
my key不能是字符串常量(即:“Bob”),否则不会有任何其他引用,GC将从my map中清除该对象


是否有其他缓存结构提供所有这些功能?

您的推理是正确的

  • Map
    用于“延迟”键不会出现问题的情况,因为它们比存储在Map中的对象小得多。在这样的情况下,将大量键映射到清空的弱引用不会使系统的内存资源紧张到让您注意到的程度

  • 您可以使用
    WeakHashMap
    处理希望垃圾收集密钥的情况,但使用
    String
    常量是绝对正确的:这确实会破坏目的。通常,您使用的自定义键类型会覆盖具有引用相等性的
    equals

  • 如果该键也不是弱引用,那么一旦对象被GC'ed,哈希映射中仍然会有一个键指向null的条目

  • 键指向指向指向空引用的非空的
    WeakReference

    因此,映射仍将包含相同数量的行,只是所有行都指向空值

    不,见上文

    解决方案是使用一个
    WeakHashMap
    ,它有一个后台活动,通过
    ReferenceQueue
    发现正在收集的弱密钥,并删除相应的映射。更好的是,一个
    WeakHashMap
    。然而:

  • 如果键是字符串文字,它们无论如何都不可收集

  • 将其描述为内存泄漏有点夸张。映射本身是内存泄漏。如果您不希望引用对象,请不要将它们存储在映射中。

    您是对的,引用对象的集合不会删除映射,但是,结果并不等同于

    map.put("Bob", null );
    
    map.put("Bob", new WeakReference<>(null) );
    
    这相当于

    map.put("Bob", null );
    
    map.put("Bob", new WeakReference<>(null) );
    
    假设
    user.getUsername()
    返回对
    user
    对象中存储的字符串实例的引用,以确保只要
    user
    是强可访问的,它就保持强可访问性,从而获得所需的语义

    我看不出有任何限制。由于存储在
    用户
    中的字符串实例确实存在,因此只要
    用户
    实例存在,引用与map key完全相同的字符串实例就没有开销。您仍然可以使用字符串常量作为查找键ala
    User u=map.get(“Bob”)
    ,因为它们的相等性仍然是根据
    String.hashCode()
    String.equals()
    确定的。如果
    使用字符串常量
    “Bob”
    放置
    映射,则映射通常至少会持续到包含该常量的代码(以及在此生存期内使用相同文本的其他代码)处于活动状态时,很可能会持续到整个应用程序。但是使用不同的字符串实例作为键(而不是存储在referent中)有什么意义呢


    请注意,
    WeakHashMap
    必须处理相同的问题,条目不会自动删除。它必须使用来发现何时收集了引用,以从表中删除其关联项。每当您在表上调用方法时都会发生,因此当您不在表上调用方法时,表不会得到清理,但由于每次插入都会发生此清理,因此可以防止不断增长的情况发生。

    为什么字符串文字不可收集?它们不只是遵循相同规则的对象吗?仅仅因为字符串是不可变的,我不明白为什么它们会被区别对待。@Eric B:规范要求将相同的字符串文本解析到同一个实例,该实例是使用intern表实现的。虽然此表引用的字符串是可收集的,但检测到的由文本引用的字符串实例的中间集合与始终解析到同一实例的指定行为不匹配。因此,只有当包含文本的所有代码也被收集时,才可能收集这样的字符串,以使收集发生时无法检测到。对于应用程序类加载器加载的代码,这种情况永远不会发生。@holger谢谢。我意识到,就在我阅读下面你的回答时。做
    map.put(newstring(“Bob”)、newweakreference(user))
    会不会绕过字符串文字问题,或者这被认为是同一件事?考虑到我有意创建一个字符串对象,我希望它将其视为字符串对象,而不是文字对象,但我不确定。@Eric B:它将解决字符串文字问题,但会创建一个新问题。由于没有任何内容引用通过
    new string(“Bob”)
    创建的新字符串实例,因此它可以立即进行垃圾收集,因此
    WeakHashMap
    可以在下次查询它时删除该映射。这正是“有意创建字符串对象”的含义;该实例不同于表示文本的对象,也不同于用户引用的对象