关于java:你什么时候使用WeakHashMap或WeakReference?

When would you use a WeakHashMap or a WeakReference?

弱引用的使用是我从未见过的实现,因此我正在尝试找出它们的用例是什么,以及实现如何工作。你什么时候需要使用WeakHashMapWeakReference,它是如何使用的?


One problem with strong references is
caching, particular with very large
structures like images. Suppose you
have an application which has to work
with user-supplied images, like the
web site design tool I work on.
Naturally you want to cache these
images, because loading them from disk
is very expensive and you want to
avoid the possibility of having two
copies of the (potentially gigantic)
image in memory at once.

Because an image cache is supposed to
prevent us from reloading images when
we don't absolutely need to, you will
quickly realize that the cache should
always contain a reference to any
image which is already in memory. With
ordinary strong references, though,
that reference itself will force the
image to remain in memory, which
requires you to somehow determine when
the image is no longer needed in
memory and remove it from the cache,
so that it becomes eligible for
garbage collection. You are forced to
duplicate the behavior of the garbage
collector and manually determine
whether or not an object should be in
memory.

了解弱参考,伊桑·尼古拉斯


要清楚的一个区别是WeakReferenceSoftReference之间的区别。

基本上,只要被引用对象没有硬引用,JVM就会急切地使用一个WeakReference。另一方面,在垃圾收集器真正需要回收内存之前,SoftReferenced对象往往会被留下来。

值保存在WeakReference中的缓存将非常无用(在WeakHashMap中,它是弱引用的键)。当您想要实现一个缓存时,SoftReferences非常有用,该缓存可以随着可用内存的增长和收缩而增长和收缩。


WeakReferences和WeakHashMaps的一个常见用途是向对象添加属性。有时,您希望向对象添加一些功能或数据,但子类化和/或组合不是一个选项。在这种情况下,最明显的做法是创建一个哈希映射,将要扩展到要添加的属性的对象链接起来。然后,只要你需要这个财产,你就可以在地图上查到它。但是,如果要添加属性的对象往往会被销毁并创建很多,那么最终可能会在地图中有许多旧对象占用大量内存。

如果您使用WeakHashMap代替,对象一旦不再被程序的其余部分使用,它们就会离开您的映射,这是所需的行为。

我必须这样做才能向java.awt.Component中添加一些数据,以避免jre在1.4.2和1.5之间发生变化,我可以通过对感兴趣的每个组件(JButtonJFrameJPanel)进行子类化来修复它,但使用更少的代码就容易得多。


对于WeakHashMapWeakReference,另一个有用的例子是侦听器注册表实现。

当你创建了一些想要听某些事件的东西时,通常你注册一个监听器,例如。

1
manager.registerListener(myListenerImpl);

如果manager将侦听器存储在WeakReference中,这意味着您不需要删除寄存器,例如使用manager.removeListener(myListenerImpl)中的寄存器,因为一旦侦听器或保存侦听器的组件不可用,它将自动删除。

当然,您仍然可以手动删除您的侦听器,但是如果您不删除或忘记它,它不会导致内存泄漏,也不会阻止您的侦听器被垃圾收集。

WeakHashMap是在哪里出现的?

希望将已注册的侦听器存储为WeakReferences的侦听器注册表需要一个集合来存储这些引用。在标准Java库中没有EDOCX1 14实现,只有EDOCX1×1,但是我们可以很容易地使用后者来实现第一个功能:

1
2
Set<ListenerType> listenerSet =
    Collections.newSetFromMap(new WeakHashMap<ListenerType, Boolean>());

使用这个listenerSet来注册一个新的侦听器,您只需将它添加到集合中,即使它没有被显式删除,如果侦听器不再被引用,它也将被JVM自动删除。


这篇博客文章演示了这两个类的用法:Java:在ID上同步。用法如下:

1
2
3
4
5
6
7
8
private static IdMutexProvider MUTEX_PROVIDER = new IdMutexProvider();

public void performTask(String resourceId) {
    IdMutexProvider.Mutex mutext = MUTEX_PROVIDER.getMutex(resourceId);
    synchronized (mutext) {
        // look up the resource and do something with it
    }
}

IDmutextProvider提供基于ID的对象以进行同步。要求如下:

  • 必须返回对同一对象的引用才能同时使用等效ID
  • 必须为不同的ID返回不同的对象
  • 没有释放机制(对象不会返回到提供程序)
  • 不能泄漏(未使用的对象可以进行垃圾收集)

这是通过使用类型为的内部存储映射实现的:

1
WeakHashMap<Mutex, WeakReference<Mutex>>

对象既是键又是值。当映射外部没有对象的硬引用时,可以对其进行垃圾收集。映射中的值存储在硬引用中,因此必须将该值包装在weakreference中以防止内存泄漏。最后一点在javadoc中介绍。


如上所述,只要存在强引用,弱引用就被保留。

一个示例用法是在监听器内部使用weakreference,这样监听器在其目标对象的主引用消失后就不再活动。注意,这并不意味着weakreference会从监听器列表中删除,清理仍然是必需的,但可以在预定的时间执行。这也有防止被监听对象持有强引用并最终成为内存膨胀源的效果。示例:Swing GUI组件引用的模型的生命周期比窗口长。

当我们像上面所描述的那样和听众一起玩的时候,我们很快意识到,从用户的角度来看,对象被"立即"收集。


例如,如果您希望跟踪从某个类创建的所有对象。为了仍然允许对这些对象进行垃圾收集,可以保留对对象的弱引用的列表/映射,而不是对象本身。

现在如果有人能解释幽灵对我的引用,我会很高兴…


我在Weakreferences中的一个实际用途是,如果你有一个非常大的,很少使用的物体。您不希望在不需要的时候将它保存在内存中;但是,如果另一个线程需要相同的对象,您也不希望在内存中有两个对象。可以在某个地方保留对对象的弱引用,在使用它的方法中保留硬引用;当两个方法都完成时,将收集对象。


我在谷歌代码中搜索了"new weakhashmap()"。

我从GNU类路径项目中得到了一堆匹配项,

  • Apache Xbean项目:weakhashmapeditor.java
  • Apache Lucene项目:cachingWrapperFilter.java

  • 您可以使用weakhashmap实现一个用于扩展对象创建的无资源缓存。

    但请注意,不希望有可变对象。我用它将查询结果(执行大约需要400毫秒)缓存到一个文本搜索引擎中,这个引擎很少更新。