我发现了一段代码,我认为它会导致意外的内存泄漏:
Object user = getUser("Bob");
Map<String, WeakReference<Object>> map = new HashMap<>();
map.put("Bob", new WeakReference( user ) );
这张地图的目的是为了缓存对象,并在它们不再被强引用时,由GC自动从地图中清除。然而,我认为如果键也不是弱引用,那么一旦对象被GC回收,哈希映射中仍将存在一个指向null的键值对。因此,地图仍然包含相同数量的行,只是所有行都指向null值。因此,在上面的示例中,一旦所有对user的强引用都被释放并且GC销毁了对象,地图中的条目将等同于:
map.put("Bob", null );
除非有清理例程刷新所有键的空值,否则我的映射将继续增长。
因此问题变成了如何解决?是否有一种可以自动刷新条目的映射构造可用于如果值被销毁?
我考虑过做这样的事情:
Object user = getUser("Bob");
Map<String, WeakReference<Object>> map = new WeakHashMap<>();
map.put(user.getUsername(), new WeakReference( user ) );
但这似乎是一个非常有限的用例,我的键必须是从我的值中检索出来的对象。使用WeakHashMap
时,我的键不能是字符串常量(即:"Bob"),否则就不会有其他引用它的地方,垃圾回收器将从我的映射中清除该对象。
是否有其他缓存结构可以提供所有这些功能呢?
map.put( new String("Bob"), new WeakReference(user))
,是否可以规避字符串字面值问题,或者这被认为是同样的事情?考虑到我有意创建一个字符串对象,我希望它将其视为对象而不是字面值,但我不确定。 - Eric B.new String("Bob")
创建的新字符串实例,因此它立即符合垃圾回收条件,因此WeakHashMap
可能会在下次查询时删除映射。这正是“有意创建字符串对象”的含义;该实例与表示字面量的对象不同,并且与User
实例引用的对象也不同。 - Holger