java 中集合的弱引用

WeakReference of a Collection in java

背景故事

在我维护的库中,我们有一个内部地图来跟踪我们的缓存。 库的用户有兴趣获得此地图的列表访问权限,但我们只能通过复制其内容来提供此功能(线程安全原因)。 这个想法是在第一次访问时缓存这个列表,而不会在第二次访问时有太多的内存开销。 举例说明:

List<Bob> list = cache.asList();
List<Bob> otherList = cache.asList(); // use from cache, if still available

问题是,如果不再需要,我们不想永远保留此列表。由于 java 使用 GC,我们认为为此使用 WeakReference 是合适的,以便在未收集时允许使用它。

问题

如果我的 class 中存储了一个 WeakReference<List<Bob>>,如果其中一个元素变得弱可达(这意味着列表是弱可达的),会发生什么情况? GC 是否可能决定只收集列表中的元素,或者它会寻找所有其他引用它的弱可达对象并收集它们,在本例中是列表?

问题是,如果 GC 收集了列表的一个元素,然后我们尝试再次访问该列表(如果可能的话)会发生什么?

澄清

我对列表的可达性不感兴趣,我知道列表在 WeakReference 内并且元素与其可达性无关。我关心一个特定的状态,其中列表和列表的元素都是弱可达的,以及 GC 是否可能只收集元素而不收集列表本身。 GC 在这种特定情况下究竟做了什么?

只要列表本身不是弱可达的,它的元素也不会弱可达。 (假设列表实现本身不使用弱引用或类似的)

所以用弱引用缓存列表没有问题,因为它要么完全被垃圾收集,要么根本不被垃圾收集。

在提供的情况下(WeakReference<List<Something>>)你只有这样的可能情况:

public class Test {

    private WeakReference<List<String>> listWeakReference;

    public Test(final WeakReference<List<String>> listWeakReference) {
        this.listWeakReference = listWeakReference;
    }

    public static void main(String[] args) {
        List<String> testList = Arrays.asList("a", "b", "c");

        Test test = new Test(new WeakReference<>(testList));

        // Initial check
        System.out.println(test.listWeakReference.get());

        // Call gc and check
        System.gc();
        System.out.println(test.listWeakReference.get());

        // Remove reference and call gc
        testList = null;
        System.gc();
        System.out.println(test.listWeakReference.get());
    }
}

首先,SoftReference 更适合缓存,即使这样也不是很好。

WeakReference 可能会被释放 立即 引用变得弱可达。但是,它可能要等到执行的某个时候才会这样做——也就是说,它不会在广泛的测试期间发生,但会在生产中发生。娱乐时间。 NetBeans 曾经在其文件缓存中执行此操作。当然,其余代码需要缓存,因此以令人难以置信的频率获取和释放引用。使用该应用程序一段时间后,它会突然锤击文件 I/O 并变得无法使用。

为了获得最佳性能,您需要明确估计进程使用了​​多少内存并在必要时释放。不容易。

回到问题。收集 WeakReference(和 SoftReference 的内容是一个两阶段操作。第一阶段只是清除 Reference(如果您正在使用它,则清除队列)。不收集关联的内存。内存可以通过终结器复活。WeakReference永远被清除并排队,不会重置。只有当一个对象完全不可达时,关联的内存才会作为一个单独的阶段被收集。

不用担心,Java 是 内存安全的(错误除外)。