虚引用的作用是什么?

  • Post category:Java

虚引用是Java中4种引用类型之一,它的作用主要是用于跟踪垃圾回收器回收对象的生命周期。当一个对象仅仅持有虚引用时,它就相当于未被引用。它的唯一作用是在这个对象被垃圾回收器回收时,能够收到一个系统通知,以便进行必要的资源清理操作。换句话说,虚引用提供了一种机制,使得我们可以在对象被垃圾回收器回收时,清理一些资源。

使用虚引用需要借助于java.lang.ref包中的PhantomReference类。其构造方法如下:

public PhantomReference(T referent, ReferenceQueue<? super T> queue)

其中,referent参数是被引用的对象,而queue则是用于接收回收通知的队列。我们可以通过该队列的remove()方法来获取被回收的对象。

以下是两个示例说明:

  1. 在垃圾回收时清理一些外部资源

假设有一个文件管理器类FileManager,其中的文件对象可以通过getFile()方法获取。我们希望在文件对象被回收时,同时删除磁盘上对应的文件。可以通过虚引用来实现:

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.nio.file.Files;
import java.nio.file.Path;

public class FileManager {
    private final ReferenceQueue<File> queue = new ReferenceQueue<>();

    public class FileManagerReference extends PhantomReference<File> {
        private final Path filePath;

        public FileManagerReference(File file, Path filePath) {
            super(file, queue);
            this.filePath = filePath;
        }

        public void cleanup() {
            if (Files.exists(filePath)) {
                try {
                    Files.delete(filePath);
                } catch (IOException e) {
                    // 忽略删除失败的情况
                }
            }
        }
    }

    public File getFile(String path) {
        File file = new File(path);
        FileManagerReference reference = new FileManagerReference(file, file.toPath());
        // 将虚引用存放到WeakReference列表中,这样referent将被赋值为null
        return reference.get();
    }

    public void processQueue() {
        FileManagerReference reference;
        while ((reference = (FileManagerReference) queue.poll()) != null) {
            reference.cleanup();
        }
    }
}

getFile()方法中,我们创建了一个FileManagerReference虚引用,并将它添加到列表中。由于引用类型的特性,FileManagerReference对象会在被垃圾回收时放入queue队列中。在processQueue()方法中,我们可以从队列中获取一个FileManagerReference对象,并调用它的cleanup()方法来删除磁盘上对应的文件。

  1. 检测对象的生命周期

假设有一个订单管理器类OrderManager,其中的订单对象可以通过getOrder()方法获取。我们希望在订单对象被回收时,能够及时得到通知并记录日志。可以通过虚引用来实现:

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class OrderManager {
    private final ReferenceQueue<Order> queue = new ReferenceQueue<>();

    public class OrderReference extends PhantomReference<Order> {
        private final String orderId;

        public OrderReference(Order order, String orderId) {
            super(order, queue);
            this.orderId = orderId;
        }

        public void logEvent(String event) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            pw.println("Event: " + event);
            pw.println("Order ID: " + orderId);
            pw.println("Timestamp: " + System.currentTimeMillis());
            pw.println();
            System.out.println(sw.toString());
        }
    }

    public Order getOrder(String orderId) {
        Order order = new Order(orderId);
        OrderReference reference = new OrderReference(order, orderId);
        // 将虚引用存放到WeakReference列表中,这样referent将被赋值为null
        return reference.get();
    }

    public void processQueue() {
        OrderReference reference;
        while ((reference = (OrderReference) queue.poll()) != null) {
            reference.logEvent("Order destroyed");
        }
    }
}

getOrder()方法中,我们创建了一个OrderReference虚引用,并将它添加到列表中。由于引用类型的特性,OrderReference对象会在被垃圾回收时放入queue队列中。在processQueue()方法中,我们可以从队列中获取一个OrderReference对象,并调用它的logEvent()方法来记录日志。

注意,在以上两个示例中,为了使虚引用得到正确的处理,需要定期调用processQueue()方法来处理队列。可以通过一个单独的线程来进行处理,或者在业务逻辑中定期调用。