如何说服Java Garbage Collector在工作集很小时运行?

时间:2013-10-17 00:22:02

标签: java garbage-collection

这是另一个“请告诉我如何强制Java垃圾收集器运行”的问题。在我们的申请中,我认为我们有充分的理由这样做。

这是一个服务器应用程序,通常有大约500万个活动对象。每5分钟一次,我们执行一项需要约60秒的分析任务。如果在分析运行时触发了完整的GC,则会有大约40M的活动对象。分析完成后,额外的35M对象将变为垃圾。服务器必须始终对请求保持响应(即使在分析运行时)。

我们发现,如果在分析未运行时调用完整GC大约需要1.5秒,但在分析运行时大约需要15秒。不幸的是,我们的分配模式使得完整的GC通常在分析期间触发,即使分析仅在20%的时间运行。 (每三次或第四次分析运行会触发完整的GC。)

如果旧版本的可用空间低于某个阈值(5GB),我在开始分析运行之前添加了代码来调用备受诟病的System.gc()。好处是非常可观的:我们得到1.5秒的暂停时间而不是15秒的暂停时间,我们将更多的垃圾带入讨价还价。但是,有时会忽略System.gc()调用,几分钟后,当GC自动触发时,我们会暂停15秒的暂停。

我的问题是:我们能做些什么来更有力地说服垃圾收集器运行?我们正在运行1.7.0_09-icedtea并使用Parallel GC。我想要(a)手动强制垃圾收集的可靠方法,或者(b)调整收集器的某种方式,以便它做出更智能的自动决策。 (b)看起来很难,因为我不清楚收藏家如何发现我们的工作集以这种戏剧性的方式变化。

如果需要,我愿意诉诸大量的hackery;这对我们来说是一个严重的问题。 (我们可能会将CMS或G1压缩机视为替代方案,但我对CMS的吞吐量影响持怀疑态度,并且G1在面对我们使用的大字节数组时表现不佳。)

附录:在制作方面,我们迄今为止的经验是System.gc()通常确实会触发完整的垃圾回收;至少,在我们称之为的情况下。 (我们每10到30分钟只调用一次,堆有点但不完全充满垃圾。)能够更可靠地触发垃圾收集会很好,但它在大多数时间都在帮助我们。

4 个答案:

答案 0 :(得分:6)

您的问题是您在同一JVM中运行两个具有完全不同要求和内存配置文件的应用程序。

在非面向用户的过程中单独运行数据分析,以便面向用户的服务器始终保持响应。我假设定期分析生成某种摘要或结果数据;通过将其发送到面向用户的服务器,使其可供最终用户使用,以便从那里提供服务,或者让您的前端从分析服务器单独获取它。

答案 1 :(得分:2)

考虑使用非托管内存,即ByteBuffer代替字节数组。

我只能提供一个需要调整的hack,然后可能会或可能不会工作。我首先尝试更理智的解决方案。如果要强制GC,可以通过分配大量内存来完成。这样做可以立即回收内存,但是无法优化整个分配(类似sum += new byte[123456].hashCode()应该这样做)。您需要找到一种可靠的方法来确定何时停止。带有终结器的对象可能会告诉您或者观看runtime.getFreeMemory可能有所帮助。

答案 2 :(得分:1)

我不想直接回答你的问题(我不能),而是提供一种可能的选择。

听起来你在分析运行期间分配了大量的大字节数组,然后允许它们在运行结束时被垃圾收集(或者试图在下次运行之前强制它们被垃圾收集) )。

相反,如果可能的话,尝试管理自己的字节数组池,以便在最好的情况下,在应用程序首次启动时分配所有需要的数组,然后它们在应用程序的生命周期中存活,而且不需要垃圾收集。

这个想法当然可以扩展到更复杂的数据结构和对象实例。

除了在需要时分配内存以及在不需要时“释放”内存时,这要做的工作要多得多,但是应该大大减少垃圾收集器需要做的工作。

答案 3 :(得分:-1)

我发现java GC对大量对象(20-100m对象)的处理非常糟糕。 如果那些物体真的还活着,你的情况会更糟,因为即使实际上没有任何东西可以收集,GC也会很糟糕。

解决方案是减少对象的数量(而不是您正在使用的总内存)。 我敢说你的分析阶段是使用集合和许多原始包装器(Integer,Long等)。 如果是这种情况,一种解决方案是切换到原始集合库。 我创建了一个这样的库来解决我遇到的类似问题,我在那里长时间运行100米活动对象的模拟。 该库名为Banana,有关详细信息,请参阅维基。