我相信我终于理解了WeakReference
,我想确保我没有弄错。
考虑一个名为Map<Client,Callback> clientMap
的{{1}}类的private
属性Singleton
,我们将其用于将已注册的客户端映射到各自的回调。
现在考虑以下代码:
MyService
到目前为止,即使// We register a client and it's callback
MyService.getInstance().register(client,callback);
// .. some code happens ..
// At this point, we decide client no longer lives anymore
client = null;
现在指向client
,null
仍然会引用之前指向的地址clientMap
,导致记忆泄漏。
如果问题1为YES,那么继续:我们决定防止内存泄漏,我们也应该从客户端地图中删除该对象:
client
现在我们修复了内存泄漏问题。
如果问题2为是:由于某些无法解释的问题,我们无法真正致电client = null;
MyService.getInstance().unregister(client); // This methods calls Map#remove(Client)
,因此这就是Map#remove()
的来源。
使用WeakReference
,参考计数器不会增加,一旦我们设置Map<WeakReference<Client>, WeakReference<Callback>> clientMap
,它就会自动将其从client = null;
答案 0 :(得分:0)
是和否。当所有引用(不包括弱引用)时,对象可用于下一次扫描的垃圾收集。在你的情况下,它不会按预期工作,因为Map仍然包含WeakReference的条目,指向什么都没有,并映射到指向什么都没有的WeakReference,我相信没有办法删除该元素而不迭代所有键/值。
你的上一个例子的第二个问题是Callback现在也是一个WeakReference,这意味着它可以被垃圾收集,即使它仍在使用中,只要它不存在其他引用。这听起来并不像你想要的那样。
Client client = ...;
Callback callback = ...;
Map< WeakReference< Client >, WeakReference< Callback > > map = ...;
map.put( new WeakReference< Client >( client ),
new WeakReference< Callback >( callback ) );
callback = null;
// Uh oh, callback can now be collected but client is still connected
最佳解决方案是在不再需要时从所有集合中明确删除元素。这消除了您从未真正想要依赖的对GC的依赖,并且集合中没有悬空条目需要稍后清理。
另一种解决方案是使用WeakHashMap。这似乎完全符合您的要求,它将在回收密钥时处理任何悬空条目。它确实伴随着它自身的陷阱。它不能保证何时回收这些条目,并且它不适用于循环引用,因为值仍然通过强引用引用。这意味着如果Callbacks保持对其客户端的引用,它将永远不会被回收,因为Callback是强引用强引用Client,等等。
澄清为什么&#39;何时&#39;是WeakHashMap的一个问题,是WeakHashMap可以随时清理悬空条目。只要在没有开发人员干预的情况下完成。这意味着在不再需要值之后很长时间内仍可以强烈引用这些值。
答案 1 :(得分:0)
这就是WeakHashMap存在的原因。无需执行Map&lt; WeakReference&lt; Blah&gt;,&lt; WeakReference&lt; Blah&gt;&gt;。只需使用WeakHashMap,它就会自我清理。在WeakHashMap中,它是微弱的键,它很好,但值是一个硬参考。这很好,因为当密钥被回收时,该值将被删除。阅读文档以了解其重要性的警告。
现在,真正的WeakReference不会阻止垃圾收集器清理它指向的内存。当它发生时取决于VM。通常情况下,它会很快清理它,但在负载下它会让它一直徘徊,直到绝对不得不让内存消失。这是垃圾收集器在清理内存时所做的最后一件事(如果我没记错的话)。
注意以这种方式注册回调,因为通常大多数回调是注册和遗忘,因为忘记引用因此,只有一件事持有对弱引用的回调的引用。而且它几乎立即得到了GC,而没有给你回电话。因此,如果您这样做,请确保在其他地方保留回调。