Java 垃圾回收器 - 无法定期正常运行
我有一个不断运行的程序。通常,它似乎是垃圾回收,并保持在大约8MB的内存使用量下。但是,每个周末,除非我明确调用它,否则它都拒绝垃圾回收。但是,如果它接近最大堆大小,它仍将进行垃圾回收。但是,注意到此问题的唯一原因是因为它实际上在一个周末因内存不足而崩溃,即它必须已达到最大堆大小,并且未运行垃圾回收器。
下图(单击以查看)是程序一天内内存使用情况的图形。在图表的侧面,您可以看到程序内存使用情况的正常行为,但第一个大峰值似乎是从周末开始的。这个特定的图是一个奇怪的例子,因为在我对垃圾回收器进行显式调用后,它成功运行,但随后它又爬回最大堆大小,并成功地在自己的垃圾回收上两次。
这是怎么回事?
编辑:
好吧,从评论来看,似乎我没有提供足够的信息。该程序只是接收UDP数据包流,这些数据包被放置在队列中(设置为最大大小为1000个对象),然后对其进行处理以将其数据存储在数据库中。平均而言,它每秒获得约80个数据包,但峰值可达150个。它在Windows Server 2008下运行。
问题是,这个活动是相当一致的,如果有的话,在内存使用开始稳步攀升时,活动应该更低,而不是更高。请注意,我上面发布的图表是我唯一一个延伸到那么远的图表,因为我只更改了Java Visual VM包装器,以使图表数据保持在足够远的位置,以便在本周看到它,所以我不知道它是否每周都完全相同,因为我不能在周末观看它, 因为它在专用网络上,而且我周末不在工作。
这是第二天的图表:
这几乎就是一周中每隔一天的内存使用情况。由于此问题,该程序永远不会重新启动,我们仅在星期一早上告诉它进行垃圾回收。有一周,我们尝试在周五下午重新启动它,它仍然在周末的某个时候开始攀升,因此我们重新启动它的时间似乎与下周的内存使用量没有任何关系。
当我们告诉它时,它成功地垃圾回收了所有这些对象,这一事实向我暗示了这些对象是可收集的,它只是在达到最大堆大小之前没有这样做,或者我们显式调用垃圾回收器。堆转储不会告诉我们任何事情,因为当我们尝试执行一个堆转储时,它会突然运行垃圾回收器,然后输出一个堆转储,这当然在这一点上看起来完全正常。
所以我想我有两个问题:为什么它突然不像本周剩余时间那样进行垃圾回收,为什么有一次,当它达到最大堆大小时发生的垃圾回收无法收集所有这些对象(即为什么会有对这么多对象的引用,以至于一次, 当每隔一段时间都不能有)?
更新:
今天上午是一个有趣的上午。正如我在评论中提到的,该程序正在客户端的系统上运行。我们在客户组织中的联系人报告说,凌晨1点,这个程序失败了,他今天早上上班时不得不手动重新启动它,并且再次,服务器时间不正确。这是我们过去与他们遇到的一个问题,但直到现在,这个问题似乎从未相关过。
通过查看我们的程序生成的日志,我们可以推断出以下信息:
- 在 01:00,服务器以某种方式重新同步了它的时间,将其设置为 00:28。
- 在 00:45(根据新的不正确的服务器时间),程序中的一个消息处理线程抛出了内存不足错误。
- 但是,另一个消息处理线程(我们接收到两种类型的消息,它们的处理方式略有不同,但它们都在不断进入),继续运行,并且像往常一样,内存使用量继续攀升,没有垃圾回收(从我们再次记录的图表中可以看出)。
- 在00:56,日志停止,直到大约上午7点,当程序由我们的客户重新启动时。但是,此时的内存使用情况图仍在稳步增长。
不幸的是,由于服务器时间的变化,这使得内存使用图上的时间不可靠。但是,它似乎试图进行垃圾回收,失败了,将堆空间增加到最大可用大小,并一次杀死了该线程。现在,最大堆空间已经增加,它很乐意在不执行主要垃圾回收的情况下使用所有内容。
所以现在我问这个问题:如果服务器时间突然像它一样变化,这是否会导致垃圾回收过程出现问题?