在什么情况下会使用WeakHashMap或WeakReference?
当你想要为对象添加属性时,通常使用WeakReference和WeakHashMap。有时候你想要为一个对象添加一些功能或者数据,但是不能使用子类化或者组合的方式来实现。在这种情况下,最直接的方法就是创建一个将要扩展的对象和要添加的属性关联起来的哈希映射。然后,每当你需要这个属性的时候,可以在映射中查找。然而,如果你添加属性的对象经常被销毁和创建,就会在映射中留下很多旧对象,占用大量内存。
如果使用WeakHashMap,一旦这些对象不再被程序的其他部分使用,它们就会离开映射,这正是我们希望的行为。
我曾经使用WeakHashMap为java.awt.Component添加一些数据,以解决从1.4.2到1.5的JRE变化的问题。我本可以通过对每个我感兴趣的组件(如JButton,JFrame,JPanel等)进行子类化来解决这个问题,但这种方式更容易且代码更少。
那么WeakHashMap如何知道“它们不再被程序的其他部分使用”呢?
WeakHashMap使用弱引用作为它的键。当一个对象只通过弱引用引用时,垃圾收集器会“通知”弱引用的所有者(在这种情况下是WeakHashMap)。我建议阅读一下Java文档中关于WeakReferences和ReferenceQueues的内容,以了解这些对象如何与垃圾收集器交互。
Luke,谢谢你的解答。你能提供一段简单的代码来演示你上面的描述吗?
在Java中,只有因为Java的奇怪API,弱引用才有意义。
下面是一个简单的代码示例:
import java.util.WeakHashMap; import java.lang.ref.WeakReference; public class WeakHashMapExample { public static void main(String[] args) { // 创建一个弱引用的HashMap WeakHashMap
运行以上代码,你会看到在垃圾回收之后,WeakHashMap中的键值对会被删除。
WeakHashMap
和WeakReference
的使用场景:
- 当你想要实现一个可以根据可用内存大小来增长和缩小的缓存时,可以使用SoftReference
来包装值。
- 当你想要实现一个缓存,在没有其他引用时值会消失的时候可以使用WeakReference
。
WeakReference
和SoftReference
的区别:
- WeakReference
会在被引用对象没有硬引用时被JVM主动进行垃圾回收。而SoftReference
对象则会一直被垃圾回收器保留,直到它真正需要回收内存。
- WeakHashMap
中的弱引用指的是键,而不是值。因此,将值放在WeakReference
中的缓存将会是无用的。
对于使用WeakHashMap
作为缓存的问题,有一些不同的观点:
- 有人认为,当没有其他引用指向缓存值时,缓存值消失的缓存是没有用的。
- 但也有人认为,对于像记忆化这样的场景,一个宽松地保持缓存值的缓存是有用的。
- 有人认为,如果你有对缓存的引用,那么你为什么还需要缓存呢?
- 还有人认为,WeakHashMap
作为缓存是没有问题的,因为它可以消除一类错误,即内存泄漏。
WeakHashMap
和WeakReference
主要适用于需要根据内存情况来增长和缩小的缓存,以及在没有其他引用时值会消失的缓存。对于一些特定的应用场景,SoftReference
和WeakReference
的选择可能会有不同的观点。
当你需要在缓存中存储大型结构(如图像)时,强引用会带来一些问题。使用强引用会导致图像一直保留在内存中,而无法确定何时不再需要该图像,并将其从缓存中移除,以便进行垃圾回收。为了解决这个问题,可以使用弱引用和弱哈希表。
弱引用是一种非常有用的引用类型,它允许对象在没有强引用时被垃圾回收。当只有弱引用指向一个对象时,垃圾回收器会自动将其回收。这样,当一个图像不再被强引用引用时,它将自动从缓存中移除,并且在下一次垃圾回收时被回收。
为了实现这个功能,可以使用WeakHashMap,它是Java提供的一种特殊的哈希表实现。WeakHashMap中的键是使用弱引用存储的,这意味着当只有弱引用指向键时,该键将被自动从哈希表中移除。这样,当一个图像不再被强引用引用时,它将自动从WeakHashMap中移除。
然而,有一些注意事项需要考虑。首先,弱引用和弱哈希表的行为是不确定的,因为它们依赖于垃圾回收器的实现。不同的垃圾回收策略可能导致缓存的行为不同。其次,如果使用SWT图像,并且需要通过调用dispose()方法释放系统资源,使用WeakHashMap可能会导致问题,因为SWT不喜欢在垃圾回收器中调用dispose()方法。
为了解决这个问题,可以考虑以下两种解决方法:
1. 在缓存的finalizer中调用dispose()方法,这样当缓存被垃圾回收时,对象会被正确地释放。
2. 使用PhantomReference来跟踪并在合适的时机调用dispose()方法。这种方法更好,因为它避免了对象“复活”的问题,并且可以在另一个线程中运行dispose()方法,提高了效率。
使用弱引用和弱哈希表可以解决缓存中对象不再需要时的自动移除和垃圾回收的问题。然而,需要注意不同的垃圾回收策略可能会影响缓存的行为,并且在处理需要手动释放资源的对象时,需要考虑额外的处理方法。