21

我有一个缓存,其中包含对缓存对象的软引用。我正在尝试为使用缓存的类的行为编写功能测试,专门用于清除缓存对象时发生的情况。

问题是:我似乎无法可靠地清除软引用。简单地用完一堆内存并不能解决问题:在清除任何软引用之前,我得到了 OutOfMemory。

有没有办法让 Java 更急切地清理软引用?


在这里找到

“虽然可以保证在抛出 OutOfMemoryError 之前所有 SoftReferences 都会被清除,因此理论上它们不会导致 OOME。”

那么这是否意味着上述情况一定意味着我在某个地方有内存泄漏,某个类在我的缓存对象上持有硬引用?

4

10 回答 10

15

问题是:我似乎无法可靠地清除软引用。

这不是 SoftReferences 独有的。由于 Java 中垃圾收集的性质,不能保证任何可垃圾收集的东西实际上会在任何时间点被收集。即使有一些简单的代码:

Object temp = new Object();
temp = null;
System.gc();

不能保证第一行中实例化的对象在此时或实际上在任何时候都被垃圾收集。这只是在内存管理语言中你必须忍受的事情之一,你放弃了对这些事情的声明权。是的,这有时会使最终测试内存泄漏变得困难。


也就是说,根据您引用的 Javadocs,在抛出 OutOfMemoryError 之前绝对应该清除 SoftReferences(事实上,这就是它们的全部意义,也是它们与默认对象引用不同的唯一方式)。因此,听起来好像存在某种内存泄漏,因为您持有对相关对象的更硬引用。

如果您使用-XX:+HeapDumpOnOutOfMemoryErrorJVM 的选项,然后将堆转储加载到类似jhat的东西中,您应该能够看到对您的对象的所有引用,从而查看除了您的软引用之外是否还有任何引用。或者,您可以在测试运行时使用分析器实现相同的目的。

于 2009-01-19T11:11:41.090 回答
14

还有以下 JVM 参数用于调整软引用的处理方式:

-XX:SoftRefLRUPolicyMSPerMB=<值>

其中 'value' 是每个空闲 Mb 内存的软引用保留的毫秒数。默认值为 1s/Mb,因此如果一个对象只能软可达,那么如果只有 1Mb 的堆空间可用,它将持续 1s。

于 2009-03-02T22:46:46.770 回答
5

您可以使用这段代码强制在您的测试中清除所有 SoftReference 。

于 2010-09-28T07:20:26.197 回答
2

如果你真的想要,你可以在你的 SoftReference 上调用 clear() 来清除它。

也就是说,如果 JVM 抛出 OutOfMemoryError 并且您的 SoftReference 尚未被清除,那么这意味着您必须在其他地方对该对象进行硬引用。否则将使 SoftReference 的合同无效。否则,您永远无法保证清除 SoftReference:只要仍有可用内存,JVM 就不需要清除任何 SoftReference。另一方面,允许在下次执行 GC 循环时清除它们,即使它不需要这样做。

此外,您可以考虑查看 WeakReferences,因为 VM 在清除它们时往往更积极。从技术上讲,VM 不需要清除 WeakReference,但是如果对象被认为是死的,它应该在下次执行 GC 循环时清除它们。如果您正在尝试测试清除缓存时会发生什么,使用 Wea​​kReferences 应该可以帮助您的条目更快地消失。

另外,请记住,这两者都依赖于 JVM 执行 GC 循环。不幸的是,没有办法保证其中之一会发生。即使您调用 System.gc(),垃圾收集器也可能会认为它只是在做一些漂亮的事情并选择什么都不做。

于 2009-01-19T14:21:58.350 回答
2

在典型的 JVM 实现 (SUN) 中,您需要多次触发 Full GC 才能清理软引用。这样做的原因是因为软引用需要 GC 做更多的工作,例如一种允许您在对象被回收时得到通知的机制。

恕我直言,在应用程序服务器中使用大量软引用是邪恶的,因为开发人员无法控制它们何时发布。

于 2009-01-19T16:35:12.590 回答
1

垃圾收集和其他引用(如软引用)是非确定性的,这实际上不可能可靠地执行操作,因此此时软引用肯定会被清除,因此您的测试可以判断您的缓存如何反应。我建议您通过模拟等以更明确的方式模拟参考清除 - 您的测试将是可重复的并且更有价值,而不仅仅是让 GC 清理参考的 Hopi g。使用后一种方法是一件非常糟糕的事情,只会引入额外的问题,而不是帮助您提高缓存的质量以及它的协作组件。

于 2009-01-19T11:09:42.793 回答
0

根据文档和我的经验,我会说是的:您必须在其他地方有参考。

我建议使用一个调试器,它可以显示对一个对象的所有引用(例如调试 Java 6 时的 Eclipse 3.4),并且只检查何时抛出 OOM。

于 2009-01-19T11:11:39.403 回答
0

如果你使用 eclipse,有一个名为Memory Analyzer的工具可以让 heap dump 调试更容易。

于 2009-01-19T13:51:23.107 回答
0

缓存对象是否有终结器?终结器将为对象创建新的强引用,因此即使清除 SoftReference,内存也不会被回收,直到稍后的 GC 循环

于 2009-09-16T13:21:45.913 回答
-2

如果您有一个缓存是 SoftReferences 的 Map 并且您希望它们被清除,您可以 clear() 映射,它们都将被清除(包括它们的引用)

于 2009-03-07T06:39:16.353 回答