这篇文章将讨论一种技术,以减少应用程序等待时间造成的垃圾收集暂停的负担。 正如我几年前所写, 在JVM中无法禁用垃圾收集 。 但是,有一个巧妙的技巧可以用来大大减少长时间停顿的时间和频率。
如您所知,JVM中发生了两个不同的GC事件,称为次要和主要集合。 关于这些收集期间发生的事情,有很多可用的材料,因此,我将不着重于详细描述其机制。 我只想提醒一下,在Hotspot JVM中-在次要收集期间,会收集eden和幸存者空间,在主要收集中,保管空间也会被清理和(可能)压缩。
如果打开GC日志记录(例如-XX:+ PrintGCDetails ),则会立即注意到主要集合是您应关注的集合。 一次大型垃圾收集的时间通常比一个清洁的年轻空间大几倍。 在大型GC中,有两个方面需要更多时间才能完成。 首先,年轻空间中的幸存者被复制到老地方。 接下来,除了清除旧版本中未使用的引用外,大多数GC算法还压缩了旧空间,再次需要消耗宝贵的CPU周期。
在旧空间中放置许多对象也增加了从旧空间到年轻空间的引用更多的可能性。 当检查这些表以确定年轻空间中的对象是否适合使用GC时,这将导致更大的卡片表 ,跟踪引用并增加次要GC暂停的时间。
因此,如果我们无法关闭垃圾收集,是否可以确保这些冗长的大型GC的运行频率降低,并且从使用权空间到Young的引用计数保持较低水平?
答案是肯定的。 甚至有一些疯狂的配置已完全摆脱了主要的GC。 摆脱主要的GC事件确实是一项复杂的工作,但是减少这些长时间停顿的频率是每个部署都可以实现的目标。
我们正在考虑的策略是限制使用期限的对象数量。 例如,在典型的Web应用程序中,创建的大多数对象仅在HttpRequest期间才有用。 存在并且将始终具有更长的寿命的共享状态,但是关键在于以下事实:短暂生存的对象与长期生存的共享状态的比率非常高。
现在进行任何部署的棘手部分是了解为短期对象提供多少肘部空间,以便
- 您可以保证短期对象不会被提升为终身空间
- 您不会过度配置,从而增加了基础架构的成本
从概念上讲,做到这一点很容易。 您只需要测量在请求期间为短期对象分配的内存量,并将其与峰值加载时间相乘即可。 您将要得到的结果是您想要在eden或单个幸存者空间中容纳的内存量。 这将使GC真正有效地运行,而不会意外升级为终身保有权。 从概念层面进行放大会发现一些复杂的技术问题,我将在以后的文章中介绍这些问题。
那么从这里可以得出什么结论呢? 首先-为您的应用程序确定理想的GC配置是一项复杂的工作。 这既是坏消息,也是好消息。 不好的是–它需要您进行大量的实验。 考虑到这一点,我们很乐意解决棘手的问题,目前正在制定实验以进一步研究该领域。 有朝一日,在不久的将来, Plumbr能够为您完成此任务,从而使您免于无聊的管道工作,并使您可以专注于手头的实际问题。
翻译自: https://www.javacodegeeks.com/2014/10/reducing-the-frequency-of-major-gc-pauses.html