【问题标题】:How to cause soft references to be cleared in Java?如何在 Java 中清除软引用?
【发布时间】:2010-10-02 05:11:16
【问题描述】:

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

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

有什么方法可以让 Java 更积极地清理软引用?


找到here

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

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

【问题讨论】:

    标签: java garbage-collection soft-references


    【解决方案1】:

    问题是:我似乎不能 可靠地获得软引用 清除。

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

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

    无法保证第一行中实例化的对象在此时或实际上在任何时候都会被垃圾回收。这只是在内存管理语言中您必须忍受的事情之一,您正在放弃对这些事情的声明权。是的,这有时会导致很难明确地测试内存泄漏。


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

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

    【讨论】:

    • +1 用于 jhat 参考,看起来是一个非常有用的工具。 .NET 有类似的东西吗?
    • 回答我自己的问题:microsoft.com/downloads/…
    【解决方案2】:

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

    -XX:SoftRefLRUPolicyMSPerMB=

    其中“值”是软引用为每 Mb 空闲内存保留的毫秒数。默认值为 1s/Mb,因此如果一个对象仅可软访问,则如果只有 1Mb 的堆空间可用,它将持续 1s。

    【讨论】:

      【解决方案3】:

      您可以使用此piece of code 强制清除测试中的所有软引用。

      【讨论】:

        【解决方案4】:

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

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

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

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

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

          • 同意第一点(正确,第一次只记时钟)。不同意第二点——太笼统了。
          • 你为什么觉得它太笼统了。好吧,如果您的服务器上只有一个应用程序,那可能是这样。但我认为这是非常罕见的。即便如此,政策通常也不是您想要的。
          【解决方案6】:

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

          【讨论】:

          • 但是 OOM 时间的引用清除是定义的行为,并且几乎是他喜欢通过 SoftReferences 实现的(如果我理解正确的话),所以我想说他应该测试它.
          【解决方案7】:

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

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

          【讨论】:

            【解决方案8】:

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

            【讨论】:

              【解决方案9】:

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

              【讨论】:

                【解决方案10】:

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

                【讨论】:

                • -1。不,这与清除引用不同。它只会导致 SoftReference 变得不可访问(只要没有其他对 SoftReference 的强引用)。如果 SoftReference 被 gc'ed 作为结果,那么它将不会入队。如果您使用 ReferenceQueue 在清除 SoftReference 时通知您,那么这将阻止调用清除方法。
                • @finw,实际上答案有其优点,这可能是您能做的最好的,如果您希望收到通知,请不要使用软引用,而是使用带有软引用的幻像提供缓存机制。
                猜你喜欢
                • 2010-12-22
                • 1970-01-01
                • 1970-01-01
                • 2021-08-09
                • 2012-05-01
                • 2022-08-17
                • 2012-11-15
                • 1970-01-01
                • 2010-10-26
                相关资源
                最近更新 更多