Java垃圾收集器时间限制

Xer*_*rus 5 java garbage-collection jvm

情况

我正在为 Java 竞赛开发一个客户端,每当我收到请求时,我都有两秒钟的时间来响应。响应后到下一个请求的时间未知。

有时,找到正确的响应需要将近 2 秒,有时只需几毫秒。问题是当垃圾收集在两秒结束时发生在一个较长的计算中(它也分配了很多对象),因此响应发送太晚,我被取消资格。

使用详细的 gc 输出,我发现 gc 通常需要大约 0.6 秒,即使我试图将其限制得更低。我还尝试调用System.gc()更短的计算(因为我确信我有大约 1.8 秒的时间,我不需要做任何事情),但花了 1-3 秒,这也不安全。

我的程序很少有长期存活的对象,大多数存活时间不到一秒。

眼镜

我知道该程序将始终在具有这些可用资源的同一台机器上运行:

我当前的 jvm 参数:

java -Dfile.encoding=UTF-8 \
  -XX:MaxGCPauseMillis=200 \
  -XX:GCPauseIntervalMillis=2050 \
  -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled \
  -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 \
  -XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark \
  -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
Run Code Online (Sandbox Code Playgroud)

想法

  • 我可以以某种方式告诉 gc 它现在应该收集一些垃圾,但只能收集大约 1.5 秒吗?
  • 是否有System.gc()仅适用于年轻对象而不检查年老代的等价物?
  • 能否优化jvm参数达到更好的效果?

Xer*_*rus 3

经过大量实验后,我找到了我需要的标志,现在想分享这些知识:

  • -XX:+UseConcMarkSweepGC- 在比较了我的用例的 G1 和 CMS 的日志结果后,我确定 CMS 暂停时间较短,此外它仍然支持单线程集合
  • -XX:+ExplicitGCInvokesConcurrent调用 System.gc() 时,不会调用 Full GC,而是调用标准GC。
  • -XX:NewRatio=1老年代与年轻代的大小比例,这是最低值,因为我的长寿命对象很少
  • -mx800m -ms800m减少并固定内存大小,因此收集将更频繁地发生并且花费更少的时间。以吞吐量换取响应能力。
  • -XX:-UseParNewGC禁用收集年轻代的并行化。这是一个破坏性因素,将 GC 停止世界时间从 0.2-0.5 秒减少到 0.02-0.2 秒,因为我只有一个可用的核心。(不建议将其与 CMS 结合使用,并且可能会在较新的 Java 版本中删除)

使用这些参数,我可以将 GC 暂停时间从 0.4 秒到 3 秒减少到始终小于 0.2 秒。为了完整起见,这些标志对于调试最有用:

-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution
Run Code Online (Sandbox Code Playgroud)

请不要忘记,这些是针对非常具体的要求进行优化的:只有一个核心可用,大量新垃圾,没有旧回收,非常低的停止世界暂停

这是我可以推荐进一步阅读的小备忘单:http://blog.ragozin.info/2016/10/hotspot-jvm-garbage-collection-options.html