强引用
这些是我们每天编码的常规对象引用:
Employee emp = new Employee();
变量“emp”持有对 Employee 对象的强引用,并且可通过任何强引用链访问的对象不符合垃圾回收条件。
通常,这是您想要的,但并非总是如此。现在假设我们从一个集合或地图中的数据库中获取大量员工,并且我们需要定期对他们进行大量处理,所以为了保持性能,我们会将它们保存在缓存中。
这很好,但现在我们需要不同的数据,我们不需要那些 Employee 对象,除了缓存之外,它们不会从任何地方引用。哪个导致内存泄漏,因为这些对象没有被使用但仍然不符合垃圾回收的条件,并且我们无法从缓存中删除这些对象,因为我们没有引用它们?
所以这里要么我们需要手动清空整个缓存,这很乏味,要么我们可以使用其他类型的引用,例如弱引用。
弱引用
弱引用不会将对象固定到内存中,如果没有从其他引用中引用,它将在下一个 GC 周期中被 GC。我们可以使用 Java 提供的 WeakReference 类来创建上述类型的缓存,它不会存储没有从其他地方引用的对象。
WeakReference<Cache> cache = new WeakReference<Cache>(data);
要访问数据,您需要调用 cache.get()。如果弱引用被垃圾回收,则此 get 调用可能会返回 null:您必须检查返回的值以避免 NPE。
Java 提供了使用弱引用的集合,例如,WeakHashMap 类将键(而不是值)存储为弱引用。如果 key 被 GC 了,那么 value 也会自动从 map 中删除。
由于弱引用也是对象,我们需要一种方法来清理它们(当它们引用的对象被 GC 时,它们不再有用)。如果您将 ReferenceQueue 传递给弱引用的构造函数,那么垃圾收集器会在完成或 GC 之前将该弱引用附加到 ReferenceQueue。您可以定期处理此队列并处理死引用。
软参考
SoftReference 类似于 WeakReference,但它不太可能被垃圾回收。垃圾收集器根据内存需求自行决定清除软引用。虚拟机保证在抛出 OutOfMemoryError 之前清除所有对软可访问对象的软引用。
幻影参考
幻像引用是所有引用类型中最弱的,对它们调用 get 将始终返回 null。对象在最终确定后但在其分配的内存被回收之前被虚引用,这与在最终确定之前入队的弱引用或很少使用 GC 的虚引用相反。
那么它们有什么用呢?当你构造一个幻像引用时,你必须总是传入一个 ReferenceQueue。这表明您可以使用幻像引用来查看您的对象何时被 GC。
嘿,所以如果弱引用在被认为是 finalize 但尚未被 GC 时入队,我们可以在 finalizer 块中创建一个对该对象的新强引用并防止对象被 GC。是的,你可以,但你可能不应该这样做。为了检查这种情况,每个对象至少会发生两次 GC 循环,除非该对象只能通过幻像引用访问。这就是为什么即使你的内存包含大量垃圾,你也会用完堆。虚拟引用可以防止这种情况发生。
你可以在我的文章Types of References in Java(Strong, Soft, Weak, Phantom)阅读更多内容。