一篇非常好的关于Java的“弱引用”的文章。

WeakReference和SoftReference

Java里的引用有四种级别:strong、soft、weak、phantom。Strong reference最常见。 但是strong reference并不适用所有的场景。比如,用strong reference来实现cache时,要么自己动手 决定什么时候应该evict缓存的对象(这在某种意义上是重复了JVM的工作——进行垃圾回收),要么承担内存 泄漏的风险(缓存的对象做为strong references一直被cache自己引用着,使得这些对象一直不能被回收)。

这种情形下,可以考虑使用较弱的引用,比如WeakReference或者SoftReference, 来实现缓存(把什么时候应该evict缓存对象的决定权交给JVM)。

A weak reference, simply put, is a reference that isn’t strong enough to force an object to remain in memory.

SoftReference在使用上和WeakReference类似,不过SoftReference指向的对象相比于WeakReference要存活得更久一些。 一般来说,只要可用内存充足,soft reference指向的对象就不会被回收。所以在实现cache时,可能使用SoftReference更好一些, 使用WeakReference会使得缓存失效的概率大大增加。

An object which is only weakly reachable (the strongest references to it are WeakReferences) will be discarded at the next garbage collection cycle, but an object which is softly reachable will generally stick around for a while.

PhantomReference、ReferenceQueue和Finalization

a fundamental problem with finalization: finalize() methods can “resurrect” objects by creating new strong references to them

Well, the problem is that an object which overrides finalize() must now be determined to be garbage in at least two separate garbage collection cycles in order to be collected.

Finalizable对象(重载了finalize()的类的对象)的回收比较“慢”。 当finalizable对象可以被回收时,会被放到JVM的finalization queue里。 然后在后续的garbage collection cycle里dequeue去执行finalize()方法。 如果很“不幸地”在finalize()方法里重新“强引用”到正在被finalizing的对象,那么这个就不会被回收了。 考虑到finalizable对象常常可能会是个“大对象”,无论是“回收慢”还是“resurrect”都有可能使得程序在有很多可供回收对象的情况下依然out of memory。

一般来说重载finalize()都是为了在对象被回收时释放某种资源。 不重载finalize(),转而使用PhantomReference可以避开finalization机制的不足。 PhantomReference在初始化时,会传入一个ReferenceQueue对象, 当PhantomReference所指的对象被垃圾回收时,就会加入到这个queue里。 稍后,可以poll这个queue,发现有内容时再去释放相关的资源。

PhantomReference的get()总是返回null。所以它唯一用处就是告诉你它所指的对象被回收了(通过ReferenceQueue的方式)。 WeakReference在构造时也可以传入ReferenceQueue对象。但是它和PhantomReference不同的是:

WeakReferences are enqueued as soon as the object to which they point becomes weakly reachable. This is before finalization or garbage collection has actually happened

WeakHashMap

WeakHashMap可以用来做为一个简单的cache(⚠️可能并不适合做cache)。

An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use.

Finalization

这篇文章可以帮助理解Java里的finalization。

When obj is allocated, the JVM internally records that obj is finalizable. This typically slows down the otherwise fast allocation path that modern JVMs have.

When the garbage collector determines that obj is unreachable, it notices that obj is finalizable – as it had been recorded upon allocation – and adds it to the JVM’s finalization queue. It also ensures that all objects reachable from obj are retained, even if they are otherwise unreachable, as they might be accessed by the finalizer.

At some point later, the JVM’s finalizer thread will dequeue obj, call its finalize() method, and record that the obj’s finalizer has been called. At this point, obj is considered to be finalized.