在Java中处理内存不足情况的最佳方法是什么?

时间:2014-10-08 11:59:36

标签: java linux process garbage-collection out-of-memory

我们有一个应用程序可以生成新的JVM并代表我们的用户执行代码。有时那些内存耗尽,在这种情况下表现得非常不同。有时它们抛出OutOfMemoryError,有时会冻结。我可以通过一个非常轻量级的后台线程来检测后者,该线程在内存不足时停止发送心跳信号。在那种情况下,我们杀死了JVM,但我们永远无法确定未能获得心跳的真正原因是什么。 (它也可能是网络问题或分段错误。)

可靠地检测JVM中内存不足情况的最佳方法是什么?

  • 理论上,-XX:OnOutOfMemoryError选项看起来很有希望,但由于这个错误,它实际上无法使用:https://bugs.openjdk.java.net/browse/JDK-8027434

  • 由于众所周知的原因(例如你永远不知道它发生在哪里)捕获OutOfMemoryError实际上不是一个好的选择,尽管它在许多情况下都有效。

  • 剩下的情况是JVM冻结并且不会抛出OutOfMemoryError的情况。我仍然确定内存 是导致此问题的原因。

有没有替代方法或解决方法?垃圾收集设置使JVM自行终止而不是冻结?

编辑:我完全控制了分叉和分叉的JVM以及在这些内部执行的代码,两者都在Linux上运行,如果使用OS特定的实用程序,则可以使用这有帮助。

3 个答案:

答案 0 :(得分:2)

唯一真正的选择是(不幸的是)尽快终止JVM。

由于您可能无法更改所有代码以捕获错误并做出响应。如果您不信任OnOutOfMemoryError(我想知道为什么它不应该使用Java 8使用的vfork,并且它适用于Windows),您至少可以触发堆转储并在外部监视这些文件: / p>

java .... -XX:+HeapDumpOnOutOfMemoryError "-XX:OnOutOfMemoryError=kill %p"

答案 1 :(得分:1)

经过一段时间的实验,这是对我们有用的解决方案:

  1. 在生成的JVM中,捕获OutOfMemoryError并立即退出,通过退出代码向控制器JVM发出内存不足信号。
  2. 在衍生的JVM中,定期检查当前Runtime的消耗内存量。当使用的内存量接近于临界值时,创建一个标志文件,向控制器JVM发出内存不足情况的信号。如果我们从这个条件中恢复并正常退出,请在退出之前删除该文件。
  3. 在控制JVM加入分叉JVM之后,它检查步骤(1)中生成的退出代码和步骤(2)中生成的标志文件。除此之外,它还会检查文件hs_err_pidXXX.log是否存在并包含“Out of Memory Error”行。 (此文件由java生成,以防它崩溃。)
  4. 只有在实现所有这些检查之后,我们才能处理分叉JVM内存不足的所有情况。我们相信,从那时起,我们就没有错过这种情况发生的情况。

    由于fork问题,未使用java标志-XX:OnOutOfMemoryError,并且未使用-XX:+HeapDumpOnOutOfMemoryError,因为堆转储超出了我们的需要。

    解决方案肯定不是有史以来最优雅的代码,但是为我们完成了工作。

答案 2 :(得分:0)

如果您对应用程序和配置都有控制权,那么最好的解决方案是查找抛出OutOfMemoryError的根本原因并修复此问题,而不是通过捕获错误或仅重新启动来尝试隐藏症状的JVM。

根据您的描述,它肯定看起来JVM上运行的应用程序正在泄漏内存,只是使用配置不足的资源(在您的情况下为内存)运行,或偶尔处理需要异常大块堆的事务。这些案例的解决方案会有所不同:

  1. 如果内存泄漏,找到根本原因并由工程师修复它。用于此的工具包括堆转储分析器,分析器或检漏器
  2. 如果资源配置不足,您需要监控应用程序内存消耗,例如通过垃圾收集日志,并根据您所面对的情况调整不同内存池的大小。
  3. 如果在用户交易期间出现浪涌分配,您需要追踪导致浪涌的代码并让工程师修复它 - 通过禁用某些用户输入或以较小批量加载和处理数据。进程中的线程转储或堆转储可以指导您找到解决方案。