我正在阅读Java中的弱引用,听起来很简单,如果对象上只有弱引用,那么垃圾回收器可以收集它。除非您的引用在使用该值之前失效,该怎么办?
示例:
假设我有一个键为{1,2,3,4,5}的弱哈希图,它们的值均为1。现在假设您有一个随机数生成器,用于[1:10]中的数字。现在,每次获取数字时,它都会检查它是否在地图中为键,然后为该键提供临时的强引用。因此,使用此设置,您将拥有一些具有强引用的键,从而保留在内存中,但同时也有一些键在被选择之前会失效的可能性。
如果我对弱哈希图的直觉是正确的,这是否意味着该地图将在某个时候从其原始状态改变?
答案 0 :(得分:4)
尝试将Integer
对象用作WeakHashMap
的键可能会导致某些奇怪的行为。首先,javadoc for WeakHashMap
有以下注意事项:
该类主要用于与其equals方法使用==运算符测试对象标识的关键对象一起使用。一旦丢弃了这样的密钥,就永远无法重新创建它,因此以后无法在WeakHashMap中对该密钥进行查找,并且会惊讶地发现它的条目已被删除。此类将与其equals方法不基于对象标识的键对象(例如String实例)完美配合。但是,使用这种可重新创建的密钥对象,自动删除其密钥已被丢弃的WeakHashMap条目可能会造成混乱。
考虑以下代码:
WeakHashMap<Integer, String> map = new WeakHashMap<>();
Integer k = Integer.valueOf(9001);
map.put(k, "OVER 9000!?");
while (true)
{
System.out.println(map.get(k));
Thread.sleep(100);
k = Integer.valueOf(9001);
System.gc();
}
循环将通过打印“ OVER 9000 !?”开始,但是在第一个循环之后,原始密钥已被丢弃(即使现在已经引用了equals
的密钥)。结果,如果该关键对象被垃圾回收,则该条目将从映射中删除,并且循环将开始打印“ null”。由于我们在丢弃密钥后调用System.gc();
,因此很可能在单个循环之后发生。
不过,使用Integer
作为WeakHashMap
键并不是解决问题的方法。如果将9001以上的值更改为1,您会发现行为发生了变化! (可能吗?这可能取决于实现。)现在,该条目永远不会从映射中删除。这是因为integer cache-Integer.valueOf(1)
总是返回相同的Integer
实例,但是Integer.valueOf(9001)
每次都会创建一个新的Integer
实例。
第二个问题特定于Integer
,但是第一个问题实际上适用于您尝试使用equals
不基于==
的密钥的任何方案。而且,如果equals
是基于==
的,则您的问题就不会真正适用-如果您不再对密钥有很强的引用,它就不再适用了。不管是否从地图上删除了该值,因为您不再有办法获取它 –您无法重新创建使用基于身份的相等性的键。
答案 1 :(得分:1)
This answer已经解决了在行为取决于对象的 identity 的构造中使用基于值的等于的类型所引起的问题。可达性。
简而言之,当您能够以与弱可及键相同的方式构造新对象时,就有可能检测到键的突然移除。
但是,您也可以将弱可达对象恢复为强可达状态,例如通过调用get()
的{{1}}方法或遍历WeakReference
的映射时。
WeakHashMap
构造具有唯一标识且没有通过WeakHashMap<Object, Boolean> map = new WeakHashMap<>();
Object key = new Object();
map.put(key, true);
WeakReference<Object> ref = new WeakReference<>(key);
key = null;
// now, the key object is only weakly reachable
key = ref.get();
// now, the key object might be strongly reachable again
// in that case, this statement will print true
System.out.println(map.get(key));
重写的equals
方法的对象可确保不会存在对同一对象或相等对象的其他引用。在此代码的某一点上,该对象仅是弱可及的,但随后使其具有极高的可及性。
这些点之间可能会发生垃圾回收,并且由于原子地清除了对对象的所有弱引用,因此您可以通过从new Object()
获取null
引用来检测这种情况。但是,此时垃圾收集发生的可能性非常低。因此,链接的答案之间使用了对get()
的调用,以提高弱引用被清除的可能性。
这是一个人为的示例,但有助于解决您的问题,“……这是否意味着地图将在某个时候从其原始状态改变?”
如果您使用具有不同标识的相等密钥或在一段时间内很难访问的密钥,则地图 有时会被更改,但无法保证会发生这种情况。这取决于垃圾收集器何时运行并实际发现某些对象的弱可达性。但是通常,JVM会尝试阻止垃圾收集,直到真正需要垃圾收集为止。因此,应用程序可能会安静运行一段时间而根本不进行垃圾收集。此外,如果您确实定期轮询映射,则可能甚至会发生,因为gc在查找过程中可以很强地访问密钥的时间点正确运行。
答案 2 :(得分:0)
WeakReference
的目的是帮助进行内存管理。如您所写,“如果对象没有被正常使用”(没有强引用,实际上是持有它的直接变量),“那么您就不再需要它”(可以将其垃圾回收)。如果散列映射很弱,则它取决于密钥,因此通常将其用于缓存临时关联的数据。
话虽如此,只将某些东西放入弱哈希图中而不继续使用该键作为强引用是没有意义的,因为收集器可以在访问它之前立即收集它。
可能不会(甚至System.gc()
也不会强制GC运行),但是您不能依靠它。