我正在寻找关于如何在低延迟至关重要的环境中最好地调整年轻一代(相对于老一代)的争论。
我自己的测试倾向于表明,当年轻一代相当大时,延迟最低(例如 -XX:NewRatio <3),但是我无法将此与年轻一代越大的时间越长的直觉相协调带去垃圾收集。
应用程序在 linux 64 位,jdk 6 上运行。
内存使用量约为 50 兆字节的长生命周期对象在启动时被加载(= 数据缓存),并且从那里只有(许多)非常短的对象被创建(平均生命周期 < 1 毫秒)。
一些垃圾收集周期需要超过 10 毫秒才能运行...与应用延迟相比,这看起来真的不成比例,后者最多也只有几毫秒。
最佳答案
对于一个产生大量短生命周期垃圾而没有长生命周期垃圾的应用程序,一种可行的方法是一个大堆,几乎所有年轻一代和几乎所有伊甸园和保有期任何在 YG 收集中幸存下来的东西都超过一次。
例如(假设您有一个 32 位 jvm)
- 3072M 堆(Xms 和 Xmn)
- 128M 任期(即 Xmn 2944m)
- MaxTenuringThreshold=1
- SurvivorRatio=190(即每个幸存者空间是 YG 的 1/192)
- TargetSurvivorRatio=90(即尽可能填充那些幸存者)
您将用于此设置的确切参数取决于您的工作集的稳态大小(即每次收集时有多少存活)。这里的想法显然违背了正常的堆大小调整规则,但你没有一个以这种方式运行的应用程序。这个想法是,该应用程序主要是 v 短暂的垃圾和一些静态数据,因此设置 jvm,以便静态数据快速进入 tenured,然后有一个足够大的 YG,它不会经常被收集 v 从而最小化暂停的频率。你需要反复转动旋钮来计算出适合你的大小以及它如何与你每次收集的暂停大小相平衡。例如,您可能会发现更短但更频繁的 YG 停顿是可以实现的。
您没有说明您的应用运行了多长时间,但这里的目标是在应用的整个生命周期内完全没有永久集合。这当然可能是不可能的,但值得为之努力。
但是,在您的情况下,重要的不仅仅是收集算法,它还是分配内存的地方。 NUMA 收集器(仅与吞吐量收集器兼容并使用 UseNUMA 开关激活)利用观察结果,即对象通常仅由创建它的线程使用,因此相应地分配内存。我不确定它在 Linux 中基于什么,但它在 Solaris 上使用 MPO(内存放置优化),some details on one of the GC guys blogs
由于您使用的是 64 位 jvm,因此请确保您也使用了 CompressedOops。
鉴于对象分配率(可能是某种科学库?)和生命周期,您应该考虑对象重用。执行此操作的库的一个示例是 javalution堆栈上下文
最后值得注意的是,GC 暂停并不是唯一的 STW 暂停,您可以使用 6u21 early access 运行build 对 PrintGCApplicationStoppedTime 和 PrintGCApplicationConcurrentTime 开关进行了一些修复(可以有效地打印全局安全点的时间和这些安全点之间的时间)。您可以使用 tracesafepointstatistics 标志来了解导致它需要安全点的原因(也就是没有任何线程正在执行字节代码)。
关于java - 调整垃圾回收以实现低延迟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2781797/