gpt4 book ai didi

Java 垃圾收集器 - 无法定期正常运行

转载 作者:太空狗 更新时间:2023-10-29 22:55:29 25 4
gpt4 key购买 nike

我有一个不断运行的程序。通常情况下,它似乎是垃圾收集,并保持在大约 8MB 的内存使用量以下。但是,每个周末,除非我明确调用它,否则它拒绝垃圾收集。但是,如果它接近最大堆大小,它仍然会进行垃圾收集。然而,这个问题被注意到的唯一原因是它实际上在一个周末因内存不足而崩溃,即它必须达到最大堆大小,并且没有运行垃圾收集器。

下图 (click to see)是程序一天内存使用情况的图表。在图表的两侧,您可以看到程序内存使用的正常行为,但第一个大峰值似乎是在周末开始的。这个特殊的图是一个奇怪的例子,因为在我对垃圾收集器进行显式调用后,它成功运行,但随后它爬回最大堆大小并成功地自行收集了两次垃圾。

这里发生了什么?

编辑:

好吧,从评论来看,我似乎没有提供足够的信息。该程序只是接收一个 UDP 数据包流,这些数据包被放置在一个队列中(设置为最大大小为 1000 个对象),然后对其进行处理以将其数据存储在数据库中。平均每秒接收大约 80 个数据包,但最高可达 150 个。它在 Windows Server 2008 下运行。

问题是,这个 Activity 是相当一致的,如果有的话,在内存使用量开始稳定攀升时, Activity 应该更低,而不是更高。请注意,我上面发布的图表是我拥有的唯一一个可以追溯到那么远的图表,因为我只更改了 Java Visual VM 包装器以将图表数据保留足够远以便在本​​周看到它,所以我不知道它是否准确每周同一时间,因为我周末不能看,因为它在私有(private)网络上,而且我周末不上类。

这是第二天的图表:alt text

这几乎是一周中每隔一天的内存使用情况。该程序永远不会重新启动,并且由于这个问题,我们只在星期一早上告诉它进行垃圾收集。一周我们尝试在周五下午重新启动它,但它仍然在周末的某个时间开始攀升,因此我们重新启动它的时间似乎与下周的内存使用情况没有任何关系。

当我们告诉它时,它成功地垃圾收集所有这些对象的事实向我暗示这些对象是可收集的,它只是在达到最大堆大小之前不这样做,或者我们显式调用垃圾收集器。堆转储不会告诉我们任何事情,因为当我们尝试执行时,它会突然运行垃圾收集器,然后输出堆转储,这当然在这一点上看起来非常正常。

所以我想我有两个问题:为什么它突然不像本周剩下的时间那样进行垃圾收集,为什么在一次达到最大堆大小时发生的垃圾收集无法收集所有这些对象(即为什么一次会引用这么多对象,而每隔一次则不能引用)?

更新:

今天早上是一个有趣的一天。正如我在评论中提到的,该程序正在客户端的系统上运行。我们在客户组织的联系人报告说,凌晨1点,该程序失败,他今天早上上类时不得不手动重新启动它,而且服务器时间再次不正确。这是我们过去与他们一起遇到的问题,但直到现在,这个问题似乎从未相关过。

查看我们的程序生成的日志,我们可以推断出以下信息:

  • 在 01:00,服务器以某种方式重新同步了它的时间,将其设置为 00:28。
  • 在 00:45(根据新的、不正确的服务器时间),程序中的消息处理线程之一抛出了内存不足错误。
  • 但是,另一个消息处理线程(我们收到两种类型的消息,它们的处理方式略有不同,但都是不断进来的),继续运行,并且像往常一样,内存使用量继续攀升,没有垃圾收集(再次从我们记录的图表中可以看出)。
  • 在 00:56,日志停止,直到大约早上 7 点,我们的客户端重新启动程序。但是,此时的内存使用图仍在稳步增加。

  • 不幸的是,由于服务器时间的变化,这使得我们的内存使用图上的时间不可靠。然而,它似乎试图进行垃圾收集,失败了,将堆空间增加到最大可用大小,并立即杀死了该线程。现在最大堆空间已经增加,可以很高兴地使用所有空间而不执行主要的垃圾收集。

    所以现在我问:如果服务器时间突然像以前那样变化,会不会导致垃圾收集过程出现问题?

    最佳答案

    However the only reason this issue was noticed, is because it actually crashed from running out of memory on one weekend i.e. it must have reached the maximum heap size, and not run the garbage collector.



    我认为你的诊断是错误的。除非您的 JVM 出现严重问题,否则应用程序只会在运行完整的垃圾收集后才抛出 OOME,并发现它仍然没有足够的可用堆来继续*。

    我怀疑这里发生的事情是以下一项或多项:
  • 您的应用程序内存泄漏缓慢。每次重新启动应用程序时,泄漏的内存都会被回收。因此,如果您在一周内定期重新启动应用程序,这可以解释为什么它只在周末崩溃。
  • 您的应用程序正在执行需要不同数量的内存才能完成的计算。在那个周末,有人向它发送了一个需要更多可用内存的请求。

  • 在任何一种情况下,手动运行 GC 实际上都不能解决问题。您需要做的是调查内存泄漏的可能性,并查看应用程序内存大小,看看它是否足够大以供正在执行的任务使用。

    如果您可以长时间捕获堆统计信息,则内存泄漏将显示为完全垃圾回收后可用内存量随时间下降的趋势。 (这是锯齿图案最长“ dentry ”的高度。)与工作负载相关的内存短缺可能会在相对较短的时间内以相同的度量偶尔急剧下降趋势,然后恢复。您可能会同时看到两者,然后您可能会同时发生两种情况。

    * 实际上,决定何时放弃 OOME 的标准比这要复杂一些。它们取决于某些 JVM 调优选项,并且可以包括运行 GC 所花费的时间百分比。

    跟进

    @Ogre - 我需要更多关于您的应用程序的信息才能以任何特异性回答这个问题(关于内存泄漏)。

    有了你的新证据,还有两种可能:
  • 您的应用程序可能陷入循环,由于时钟时间扭曲而导致内存泄漏。
  • 时钟时间扭曲可能会导致 GC 认为它占用的运行时间百分比太大,并因此触发 OOME。此行为取决于您的 JVM 设置。

  • 无论哪种方式,您都应该努力依靠您的客户,让他们停止像这样调整系统时钟。 (32 分钟的时间扭曲太多了!!)。让他们安装系统服务,使时钟与网络时间逐小时(或更频繁)保持同步。至关重要的是,让他们使用带有选项的服务,可以小幅调整时钟。

    (关于第二个要点:JVM 中有一个 GC 监控机制,用于测量 JVM 在运行 GC 上花费的总时间相对于做有用工作的百分比。这是为了防止 JVM 在以下情况下停止运行您的应用程序确实内存不足。

    这种机制将通过在不同点采样挂钟时间来实现。但是如果挂钟时间在一个关键点被时间扭曲,很容易看出 JVM 可能认为特定的 GC 运行比实际花费的时间长得多……并触发了 OOME。)

    关于Java 垃圾收集器 - 无法定期正常运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4309105/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com