答案 0 :(得分:11)
然而,注意到这个问题的唯一原因是因为它实际上在一个周末因内存耗尽而崩溃,即它必须达到最大堆大小,而不是运行垃圾收集器。
我认为您的诊断不正确。除非你的JVM出现严重问题,否则应用程序只会在之后抛出一个OOME 它刚刚运行完整的垃圾收集,并发现仍然没有有足够的空闲堆来继续 * 。
我怀疑这里发生的是以下一项或多项:
您的应用程序内存泄漏缓慢。每次重新启动应用程序时,泄漏的内存都会被回收。因此,如果您在一周内定期重新启动应用程序,这可以解释为什么它只会在周末崩溃。
您的应用程序正在进行需要不同内存量才能完成的计算。在那个周末,有人向它发送了一个需要更多可用内存的请求。
在任何一种情况下,手动运行GC实际上都无法解决问题。您需要做的是调查内存泄漏的可能性,并查看应用程序内存大小,以查看它是否足够大以执行正在执行的任务。
如果您可以长时间捕获堆统计信息,则内存泄漏将显示为完全垃圾回收后可用内存量随时间的下降趋势。 (这是锯齿模式中最长的“牙齿”的高度。)与工作负荷相关的记忆短缺可能会在相对较短的时间内出现在同一测量中的偶然急剧下降趋势,随后是恢复。你可能会看到两者,然后你可以发生这两件事。
*实际上,决定何时放弃OOME的标准比这复杂一点。它们依赖于某些JVM调优选项,并且可以包括运行GC所花费的时间百分比。
<强>后续强>
@Ogre - 我需要更多关于你的应用程序的信息,以便能够以任何特异性回答这个问题(关于内存泄漏)。
根据您的新证据,还有两种可能性:
由于时钟时间扭曲,您的应用程序可能会卡在一个泄漏内存的循环中。
时钟时间扭曲可能会导致GC认为它占用了太大的运行时间百分比并因此触发了OOME。此行为取决于您的JVM设置。
无论哪种方式,你都应该精益求精在你的客户端上让他们停止调整系统时钟。 (32分钟的时间扭曲太多了!)。让他们安装系统服务,使时钟与网络时间保持同步(或更频繁)。重要的是,让他们使用带有选项的服务以小幅度调整时钟。
(重新发布第2篇:JVM中有一个GC监控机制,用于衡量JVM运行GC所花费的总时间百分比,相对于执行有用的工作而言。这是为了防止JVM磨损到当你的应用程序内存不足时暂停。
这种机制可以通过在不同点采样挂钟时间来实现。但是,如果挂钟时间在关键时刻扭曲,很容易看出JVM如何认为特定GC运行花费的时间比实际时间长得多......并触发OOME。)
答案 1 :(得分:2)
如果可能的话,我会设置进程,以便在内存不足时转储堆 - 这样你就可以分析它是否(何时)再次发生。不是答案,而是解决方案的潜在途径。
以下是JVM选项,取自Oracle的Java HotSpot VM Options页面。 (这假设您有一个Oracle JVM):
-XX:HeapDumpPath = / java_pid.hprof
堆的目录或文件名的路径 倾倒。管理。 (在1.4.2中引入 更新12,5.0更新7。)
-XX:-HeapDumpOnOutOfMemoryError
将堆转储到文件时 抛出java.lang.OutOfMemoryError。 管理。 (在1.4.2中引入 更新12,5.0更新7。)
答案 2 :(得分:2)
好的伙计们,谢谢你的帮助。然而,正确的答案与程序本身无关。
似乎在内存使用开始时它正在稳步攀升,服务器正在从内部某处同步它的时间,尽管我们客户的IT联系人不知道在哪里。显然,无论它来自哪里,都不是一个好时钟,因为时间落后半小时。我们关闭了这个同步,现在我今天早上再次检查它,问题没有发生。因此,如果系统上的时间突然改变,显然这会导致垃圾收集器出现问题。至少这对我来说意味着什么。
至于为什么这个系统的任何其他部分都没有出现在这个服务器上(也用Java编写),我们可能根本就没有注意到,因为它们没有处理大量的对象,所以他们永远不会遇到记忆状态。
我发现这很奇怪,因为我原本以为调用垃圾收集器与内存使用完全相关,而不是系统时间。显然,我对垃圾收集器如何工作的理解是非常不合适的。