调整垃圾收集以实现低延迟

时间:2010-05-06 14:24:31

标签: java garbage-collection

我正在寻找关于如何在低延迟至关重要的环境中最好地确定年轻一代(相对于老一代)的规模的论据。

我自己的测试往往表明,当年轻一代相当大时,延迟最低(例如-XX:NewRatio< 3),但是我无法将其与直觉认为年轻一代越大,时间越长应该采取垃圾收集。

应用程序在linux 64位,jdk 6上运行。

内存使用量大约是50兆字节的启动时加载的长寿命对象(=数据缓存),并且从那里开始只创建(很多)非常短暂的对象(平均寿命<1毫秒)。

一些垃圾收集周期需要超过10毫秒才能运行...与app延迟相比看起来真的不成比例,而app延迟最多只有几毫秒。

3 个答案:

答案 0 :(得分:14)

对于一个产生大量短寿命垃圾并且没有长寿的应用程序,那么一种可以工作的方法是一大堆几乎所有的年轻人和几乎所有的伊甸园和任期在YG集合中存活的任何东西都超过一次。

例如(假设你有一个32位的jvm)

  • 3072M堆(Xms和Xmn)
  • 128M终身(即Xmn 2944m)
  • MaxTenuringThreshold = 1
  • SurvivorRatio = 190(即每个幸存者空间是YG的1/192)
  • TargetSurvivorRatio = 90(即尽可能填补幸存者)

您将用于此设置的确切参数取决于工作集的稳定状态大小(即每次收集时活动的数量)。这里的想法显然违反了正常的堆大小规则,但是你没有一个以这种方式运行的应用程序。我们的想法是,应用程序主要是v短暂的垃圾和一些静态数据,所以设置jvm以便静态数据快速进入终身,然后有一个足够大的YG,以便不经常收集,从而最大限度地减少暂停的频率。你需要反复旋转旋钮来找出适合你的尺码。如何平衡每个集合的暂停大小。您可能会发现更短但更频繁的YG暂停是可以实现的。

你没有说你的应用程序运行了多长时间,但这里的目标是在应用程序的生命周期内根本没有终身收藏。当然这可能是不可能的,但值得瞄准。

然而,在你的情况下,不仅仅是收集算法很重要,而是分配内存的地方。 NUMA收集器(仅与吞吐量收集器兼容并且使用UseNUMA交换机激活)利用观察到对象通常纯粹由创建它的线程使用&amp;从而相应地分配内存。我不确定它是基于linux的,但它在Solaris上使用MPO(内存放置优化),some details on one of the GC guys blogs

由于您使用的是64位jvm,因此请确保您也使用了CompressedOops。

鉴于对象分配率(可能是某种科学库?)和生命周期,你应该考虑对象重用。执行此操作的lib的一个示例是javalution StackContext

最后值得注意的是,GC暂停不是唯一的STW暂停,你可以运行6u21 early access版本,它对PrintGCApplicationStoppedTime和PrintGCApplicationConcurrentTime开关有一些修复(有效地在全局安全点和时间之间打印时间)那些安全点)。您可以使用tracesafepointstatistics标志来了解导致它需要安全点的原因(也就是说任何线程都没有执行字节代码)。

答案 1 :(得分:5)

您是否已启用更多相关的GC设置,例如选择并发低暂停收集器算法?

从广义上讲,年轻,终身和永久的年龄需要根据您的应用程序的配置文件进行调整。如果你有许多短暂的物品,但是年轻的物体太小,很多物体都会变成终身,迫使整个终身一代的主要藏品更加频繁。同样,如果年轻人太大,那么终身教育必然会变小,并且可能会迫使频繁的主要终身收藏。

实际上,我认为你会发现,当你增加年轻一代的规模时,在次要收藏和主要收藏中花费的时间会有所不同,并且在某些时候是最佳的。

也许有用的是注意到在“大”性能敏感的服务器应用程序中,我发现有必要缩小年轻一代。这是因为这些应用程序应该已经为内存分配热点进行了分析并进行了优化,因此它们只能生成很少的短期对象。这反过来意味着年轻一代正在占据太多的堆积。

所以我想我首先进行优化,然后看看NewRatio超过8,然后观察-verbose:gc给出的输出,看看GC和Full GC时间如何交易以及它在哪里最佳。

答案 2 :(得分:1)

使用Java尝试实时应用程序时,垃圾收集调优至关重要,但还需要考虑其他方面(例如JIT编译器,定时器,线程,异步事件处理)。

由于似乎需要实时Java,Sun提供了Java实时系统规范,并提供了商业实现。您可以找到更多信息here

相关问题