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

问题描述 投票:11回答:3

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

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

  • 从理论上讲,-XX:OnOutOfMemoryError选项看起来很有希望,但由于这个bug,它实际上无法使用:https://bugs.openjdk.java.net/browse/JDK-8027434
  • 捕获OutOfMemoryError实际上并不是众所周知的理由(例如,你永远不知道它发生在哪里),尽管它在许多情况下都有效。
  • 剩下的情况是JVM冻结并且不会抛出OutOfMemoryError的情况。我仍然确定记忆是这个问题的原因。

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

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

java linux process garbage-collection out-of-memory
3个回答
2
投票

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

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

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

1
投票

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

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

只有在实现所有这些检查之后,我们才能处理分叉JVM内存不足的所有情况。我们相信,从那以后,我们没有错过这种情况发生的情况。

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

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


0
投票

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

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

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