如何检测Java堆接近满载并防止“OutOfMemoryError: Java Heap”错误?

4

在SO上有很多与“OutOfMemoryError: Java Heap”错误相关的问题,但阅读它们时,大多数似乎只是讨论如何增加堆大小或对应用程序进行分析并检测内存泄漏。

我正在处理一个项目,涉及分析分支和界算法的成本。对于小输入大小,要搜索的潜在解决方案数量以O(n!)的速度增长。在某个输入大小n处,我遇到了“OutOfMemoryError”,因为部分解决方案保留在优先队列中,直到准备好进行处理,而队列中大量的部分解决方案填满了内存。所以,我知道我没有内存泄漏,也不一定想增加堆大小。

我想做的就是简单地检测内存快要满了,然后给用户一个消息,告诉他们发生了什么,以及程序为什么退出(此时程序不必继续运行)。有没有办法做到这一点?我已经查看了java.lang.management包,但它对我来说没有太多意义,我很难找到像样的示例代码。感激任何解释或示例代码。


1
我想,不占用全部内存太简单了,太有道理了... - cHao
“不占用所有内存太容易了”……请详细说明。向其投入更多的内存并不能解决问题……对于O(n!),我可能能够处理更多的情况,但很快内存就会再次填满。请解释一下你的评论。 - neizan
1
我建议您将工作分块并拆分,仅执行您可以处理的块数量。如果您需要保存部分结果,请尝试使用文件或数据库。 - Liviu T.
1
@user1089416:我相信对于时间复杂度为 O(!n) 的算法的资源问题,通常被接受的解决方案是找到一个具有更好复杂度的算法(或者,在这方面失败时,使用一种更好的启发式算法)。 - Michael Borgwardt
5个回答

6
我想要做的就是检测内存是否接近满,然后向用户发出一条消息,告诉他们正在发生什么以及程序为什么退出(此时程序不必继续运行)。这很困难。主要是因为在进行垃圾回收之前,实际上并不知道有多少可用内存,而且严重的垃圾回收通常只会在即将耗尽内存之前才会发生。你可以在程序崩溃之后解释程序崩溃的原因:例如,Eclipse就是这样做的。您可以像处理其他Throwable一样捕获OutOfMemoryError,并显示您的消息。
 try{
      heavyLifting();
 }
 catch (OutOfMemoryError e){
    showAMessage();   
 }

很有用,谢谢。还有一个问题,"heavylifting()" 应该多久调用一次?我知道这是个不好的问题,因为它取决于我的程序,但是在方法中频繁调用它是否明智? - neizan
2
heavyLifting() 是你的算法。我的建议是把整个程序包装到这个 try/catch 块中。 - Thilo
抱歉!今天早上我很匆忙。我尝试了一下,它似乎按照我想要的方式工作。非常感谢。 - neizan
它并不总是有效。有时候内存不足以使处理器“捕捉”。 - Enyby

1
最简单和直接的解决方案是在应用程序的非常高层次上捕获OutOfMemoryError,然后只显示消息,然后关闭应用程序。
理论上,这可能会失败,因为可能没有足够的内存来显示消息,但实际上这几乎永远不会发生,因为当错误被捕获时,许多对象超出范围,几乎肯定为简单任务提供足够的内存。

感谢您的有用反馈。此外,您在我的原始问题下的评论也很有帮助。 - neizan

1

您可以使用Runtime API获取内存状态;

        // Memory status
        Runtime runtime = Runtime.getRuntime();

        long totalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();

        boolean memoryOK = minFree <= freeMemory;

1
我认为那样做不会正确,因为freeMemory()只测量已经释放的内存,而不是可供垃圾回收的对象使用的内存。你必须在之前调用System.gc(),即使这样也不能完全可靠。 - Michael Borgwardt
@Michael,同意了解当前内存状态并不能保证,垃圾回收起着重要作用。然而,OP确实询问了如何衡量内存状态。我认为回答问题并在此之后提供建议并不是什么坏事 :-) - rsp
谢谢提供这些信息,或许以后会有用,但现在Thilo的解决方案可行且简单。 - neizan

0

我认为除了使代码内存高效化之外,你无法做太多事情。即使你知道内存快要满了,现在会出现内存不足错误,你可以做的很少。在最好的情况下,你可以请求垃圾收集器变得活跃些,但这只是一个请求。请放心,JVM会尽其所能防止内存不足错误。如果它仍然出现,很可能是在给定代码的情况下无法避免的。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接