Ogr*_*gre 16 java garbage-collection
我有一个不断运行的程序.通常,它似乎是垃圾收集,并保持在大约8MB的内存使用量.然而,每个周末,它都拒绝垃圾收集,除非我明确地打电话给它.但是,如果它接近最大堆大小,它仍将是垃圾收集.然而,注意到这个问题的唯一原因是因为它实际上在一个周末因内存耗尽而崩溃,即它必须达到最大堆大小,而不是运行垃圾收集器.
下图(单击以查看)是程序一天内存使用情况的图表.在图的两侧,您可以看到程序内存使用情况的正常行为,但第一个大峰似乎是周末开始的.这个特殊的图是一个奇怪的例子,因为在我对垃圾收集器进行了一次显式调用之后,它成功运行了,但随后它又回到了最大堆大小并成功地自己垃圾收集了两次.
这里发生了什么?
编辑:
好的,从评论来看,似乎我没有提供足够的信息.该程序简单地接收UDP包,其被放置在队列中(设置为具有1000个对象的最大尺寸),然后将其加工成具有存储在数据库中其数据流.平均而言,它得到每秒约80报文,但峰值150它是运行在Windows Server 2008上运行.
问题是,这个活动是相当一致的,如果有的话,在内存使用量开始它的稳步攀升的时候,活动应该就低不就高.你要知道,我上面贴的图是唯一一个我有一个向后延伸那么远,因为我不仅改变了Java可视虚拟机的包装,以保持图形数据回远到足以目睹它这个星期,所以我不知道它是否准确每周同一时间,因为我不能看着它在周末,因为它是一个专用网络上,我不是在周末工作.
这是第二天的图表: 
这几乎是内存使用情况每周的其他日子.程序永远不会重新启动,我们只会在周一早上告诉它垃圾收集因为这个问题.一周我们尝试在周五下午重新启动它,它在周末的某个时候仍然开始爬升,所以我们重启它的时间似乎与下周的内存使用没有任何关系.
它成功地垃圾收集所有这些对象的事实,当我们告诉它暗示对象是可收集的时,它只是在它达到最大堆大小时才执行它,或者我们显式调用垃圾收集器.堆转储没有告诉我们什么,因为当我们尝试执行一个时,它突然运行垃圾收集器,然后输出堆转储,这当然看起来完全正常.
所以我想我有两个问题:为什么它突然没有垃圾收集它在本周其余时间进行的方式,为什么在一个场合,当达到最大堆大小时发生的垃圾收集无法收集所有这些对象(也就是说为什么一次都会引用这么多对象,而每隔一段时间一定不会有这样的对象)?
更新:
今天早上很有趣.正如我在评论中提到的,该程序正在客户端的系统上运行.我们在客户组织中的联系人报告说,凌晨1点,该程序失败了,他今天早上上班时必须手动重启,而且服务器时间再次不正确.这是我们过去曾经遇到的一个问题,但直到现在,这个问题似乎从来没有关系过.
查看我们的程序生成的日志,我们可以推断出以下信息:
不幸的是,由于服务器时间的变化,这使得我们的内存使用图上的时间不可靠.但是,它似乎是尝试垃圾收集,失败,将堆空间增加到最大可用大小,并立即杀死该线程.既然最大堆空间已经增加,它很乐意在不执行主要垃圾收集的情况下使用所有堆空间.
所以现在我问这个问题:如果服务器时间突然像它一样突然改变,那会不会导致垃圾收集过程出现问题?
Ste*_*n C 11
然而,注意到这个问题的唯一原因是因为它实际上在一个周末因内存耗尽而崩溃,即它必须达到最大堆大小,而不是运行垃圾收集器.
我认为您的诊断不正确.除非你的JVM出现严重问题,否则应用程序只会在运行完全垃圾收集后抛出OOME ,并发现它仍然没有足够的空闲堆来继续*.
我怀疑这里发生的是以下一种或多种情况:
您的应用程序内存泄漏缓慢.每次重新启动应用程序时,泄漏的内存都会被回收.因此,如果您在一周内定期重新启动应用程序,这可以解释为什么它只会在周末崩溃.
您的应用程序正在进行需要不同内存量才能完成的计算.在那个周末,有人向它发送了一个需要更多可用内存的请求.
在任何一种情况下,手动运行GC实际上都无法解决问题.您需要做的是调查内存泄漏的可能性,还要查看应用程序内存大小,以查看它是否足够大以执行正在执行的任务.
如果您可以长时间捕获堆统计信息,则内存泄漏将显示为完全垃圾收集后可用内存量随时间的下降趋势.(这是锯齿模式中最长的"牙齿"的高度.)与工作负荷相关的记忆短缺可能会在相对较短的时间内出现在同一测量中的偶然急剧下降趋势,随后是恢复.你可能会看到两者,然后你可以发生这两件事.
*实际上,决定何时放弃OOME的标准比这复杂一点.它们依赖于某些JVM调优选项,并且可以包括运行GC所花费的时间百分比.
跟进
@Ogre - 我需要更多关于你的应用程序的信息,以便能够以任何特异性回答这个问题(关于内存泄漏).
有了新证据,还有两种可能性:
由于时钟时间扭曲,您的应用程序可能会卡在一个泄漏内存的循环中.
时钟时间扭曲可能会导致GC认为它占用了太大的运行时间百分比并因此触发了OOME.此行为取决于您的JVM设置.
无论哪种方式,你应该瘦硬客户端上,让他们停止调整系统时钟这样的.(32分钟的时间扭曲太多了!).让他们安装系统服务,使时钟与网络时间保持同步(或更频繁).重要的是,让他们使用带有选项的服务以小幅度调整时钟.
(重新说明第2篇:JVM中有一个GC监视机制,用于衡量JVM运行GC所花费的总时间百分比,相对于执行有用的工作而言.这是为了防止JVM在停止时停止运行.你的应用程序真的没有内存.
这种机制可以通过对不同点的挂钟时间进行采样来实现.但是,如果挂钟时间在关键时刻扭曲,很容易看出JVM如何认为特定GC运行花费的时间比实际时间长得多......并触发OOME.)