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