【问题标题】:Catch OutOfMemoryError but NO heap size increasement [duplicate]捕获 OutOfMemoryError 但没有增加堆大小[重复]
【发布时间】:2011-09-01 03:56:18
【问题描述】:

我的 Java 代码包含一个方法 (getPermutations),其处理需要大量内存 (Complexity O(n!)),因此可能会引发 OutOfMemoryError。 重要的代码是这样的:

ArrayList<ArrayList<Station>> permutations = getPermutations(stations);

我不想在我的特定机器上增加堆内存大小,我也不能在其他机器上更改它。 我正在寻找的是针对此错误的某种异常处理(我很清楚通常不应该捕获错误或 throwable)。

所以我想用适当的(图形)消息对 OutOfMemoryError 做出反应,并防止我的小程序崩溃。

SwingWorker 是正确的方法吗,应该如何使用它,或者有更好的替代方案吗?

提前致谢:-)

【问题讨论】:

标签: java memory-management error-handling out-of-memory


【解决方案1】:

正如您所指出的,您不应该在程序逻辑中包含对 OutOfMemoryErrors 的处理。 OutOfMemoryError 被解释为“您的程序需要的内存比 JVM/OS 必须为您提供的更多”。

我建议您考虑返回 Iterator&lt;ArrayList&lt;Station&gt;&gt; 并懒惰地产生排列,即按需。 (您可能知道,返回集合的 size 很容易计算而无需知道实际内容。)

SwingWorker 是正确的选择吗?

很可能没有。

【讨论】:

  • 嗨,排列已经按需计算,即在图形用户界面中单击按钮时。可能是数据太多了,很有可能会报错
  • 我不是这个意思。我的意思是您应该根据需要计算 each 排列,例如在 Iterator.next() 的实现中。
  • 感谢您的回复。所考虑的方法只是递归地确定所有排列。有没有想到实现Iterator的类或者Iterator.next()应该如何实现?
  • 这里有一个建议:重写递归方法以使用显式堆栈。重构该方法,以便它不是将排列添加到结果中,而是返回排列。每次调用 next() 时,该方法都会从上次中断的地方开始,迭代并返回下一个排列。
  • 您是否还考虑使用专用线程来保持事件线程不受影响?这就是我想到 SwingWorker 的方式......
【解决方案2】:

赶上OutOfMemoryError,尽管您最终可能会遇到无法修复的情况。

请参阅此讨论:http://www.velocityreviews.com/forums/t149419-catch-outofmemory-exception.html 了解更多信息。

PS。您真的想要获取数组中的所有排列吗?考虑 aioobe 的答案,它在代码的可用性方面要好得多。

【讨论】:

    【解决方案3】:

    如果内存不足,则没有足够的堆空间来绘制窗口和显示消息。因此,抓住它对你没有帮助。就显示有用的消息而言,我建议您使用另一个进程来处理该进程的退出代码。

    但为什么不从一开始就避免内存不足错误呢?即使是小型机器也可以产生排列,你只需要牺牲运行时间。将中间结果保存到磁盘怎么样?如果您不提供库(我假设是这种情况,因为您想显示错误消息),为什么不将内部模型更改为完全基于磁盘?

    【讨论】:

    • 同意,内存不足时无法显示窗口。因此我打算在一个单独的线程中运行这个详尽的计算(这就是我想到 SwingWorker 的原因)。如果这个线程崩溃了,我的事件线程应该仍然不受影响吧?
    • 如果该线程释放了足够的内存,那么理论上您可以继续正常处理。我过去曾看到其他进程也发生过类似的事情,但这并不保证会发生。
    【解决方案4】:

    你真的没有内存了吗?如果您正在迭代处理列表并自行清理,也许。然而,也许这只是不允许 GC 完成它应该完成的工作的情况,java 不能免受内存泄漏的影响。悬挂引用、全局/静态变量等都可能导致对象在真正应该被 GC 处理时仍保留在范围内。

    运行一个分析器(其中很多可用于 Eclipse)来查看究竟是什么占用了如此大量的内存可能会很有用。

    也许小批量处理会有所帮助?在过去处理过财务数据后,我发现一次处理数百万条记录是很常见的。通常处理这些情况的最佳方法是将处理分成更小、更易于管理的块。

    【讨论】:

      【解决方案5】:

      您可以像任何其他异常一样捕获OutOfMemoryError,然后根据需要报告它。 SwingUtilities.invokeLater() 是在对话框中报告错误的好方法。恢复时会遇到问题并不是真的——从 OutOfMemoryError 中恢复通常是无痛的(当然,如果多余的对象现在有资格被收集)。

      【讨论】:

      • "从 OutOfMemoryError 中恢复通常是无痛的" 这似乎值得怀疑;您对此主张有任何参考吗?我会假设(就像这里的其他人一样)当你捕捉到 OOME 时,其他事情可能已经出错了(例如,其他线程也可能已经崩溃)。使用 Swing 的程序将有一个与主线程并行运行的 GUI 线程(Event Dispatch Thread);如果它首先崩溃,那么你就不走运了。因此,能否从 OOME 中恢复可能取决于您的程序和环境。
      【解决方案6】:

      我同意建议您分阶段扩展排列的答案。然后,你总是可以在抛出 OutOfMemoryError 时恢复:

      布尔内存标志 = 假; // 标志始终可用 尝试 { Permutations bigVar = ... // 一些初始值 while (/* 条件 */) { // 输出到目前为止发现的排列 bigVar = ... // 计算下一组扩展排列 } } 捕捉(OutOfMemoryError ex){ // bigVar 现在超出范围,垃圾也是 System.gc(); 内存标志 = 真; } // 程序测试 memoryFlag 以报告是否发生内存不足

      您的用户界面可以继续不间断地运行。这在单线程应用程序中对我有用。使用多个线程时,您将面临无法捕获错误的风险,因为每个线程都使用自己的处理程序运行,并且您可能无法控制某些 Swing 线程的范围。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-09-09
        • 2011-01-20
        • 2016-11-17
        • 2011-09-21
        • 2012-06-17
        • 2016-12-23
        • 2012-03-27
        • 2014-10-24
        相关资源
        最近更新 更多