如何在Java中清除软引用?

我有一个缓存,它具有对缓存对象的软引用。 我正在尝试为类的行为编写function测试,这些类使用缓存专门用于清除缓存对象时发生的情况。

问题是:我似乎无法可靠地获得要清除的软引用。 简单地使用一堆内存不起作用:在清除任何软引用之前,我得到一个OutOfMemory。

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


在这里找到

“虽然在抛出OutOfMemoryError之前所有SoftReferences都会被清除,但是理论上它们不会导致OOME。”

那么这是否意味着上面的场景必须意味着我的内存泄漏,某些类在我的缓存对象上持有一个硬引用?

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

这不是SoftReferences独有的。 由于Java中垃圾收集的性质,无法保证任何可垃圾收集的内容实际上都会在任何时间点收集。 即使只是简单的代码:

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

无法保证在第一行中实例化的Object在此处或实际上是任何点都是垃圾收集。 它只是你用内存管理语言生活的东西之一,你放弃了对这些事情的陈述力。 是的,这可能使得有时难以确定地测试内存泄漏。


也就是说,根据你引用的Javadocs,应该在抛出OutOfMemoryError之前清除SoftReferences(实际上,这是它们的整个点,也是它们与默认对象引用的唯一不同之处)。 因此,听起来存在某种类型的内存泄漏,因为您需要对相关对象进行更难的引用。

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

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

  -XX:SoftRefLRUPolicyMSPerMB = <值> 

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

您可以使用此代码强制在测试中清除所有SoftReferences。

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

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

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

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

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

恕我直言在应用服务器中使用大量的软件参考是邪恶的,因为开发人员对它们何时被释放没有多少控制权。

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

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

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

如果您使用eclipse,那么这个名为Memory Analyzer的工具可以使堆转储调试更容易。

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

如果你有一个缓存,它是一个SoftReferences地图,你希望它们被清除,你可以清除()地图,它们都将被清理(包括他们的参考)