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