我正在为Java竞赛开发一个客户端,每当我收到请求时,我有两秒钟的时间来回复。响应之后的时间直到下一个请求未知。
有时,找到正确的响应需要将近2秒,有时只需几毫秒。问题是当垃圾收集发生在一个较长的计算(也分配了很多对象)中,直到两秒结束时,因此响应发送得太晚而且我被取消资格。
使用详细的gc输出我发现gc通常需要大约0.6秒,即使我试图将其限制在较低的范围内。我也尝试在较短的计算上调用System.gc()
(因为我确信我有大约1.8s,我不需要做任何事情),但它需要1-3s,这也不安全。
我的程序只有很少的长寿命对象,大多数活得短于一秒。
我知道程序将始终在具有以下可用资源的同一台机器上运行:
我目前的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
System.gc()
等效,只适用于年轻的物体,不检查终身一代?以下是您可以尝试减少暂停的事情:
经过大量的实验,我找到了我需要的标志,现在想要分享这些知识:
-XX:+UseConcMarkSweepGC
- 在比较我的用例的G1和CMS的日志结果后,我确定CMS暂停时间较短,此外它仍然支持单线程集合-XX:+ExplicitGCInvokesConcurrent
在调用System.gc()时,不会调用Full GC,而是调用标准GC。-XX:NewRatio=1
老一代与年轻一代的大小比例,这是我生命对象很少的最低值-mx800m
-ms800m
减少并修复了内存大小,因此集合将更频繁地发生并且花费更少的时间。交易吞吐量以响应。-XX:-UseParNewGC
禁用并行化以收集年轻一代。这是一个交易破坏者,并将GC停止世界时间从0.2-0.5s减少到0.02-0.2s,因为我只有一个核心可用。 (不推荐将此与CMS结合使用,并且可能会在较新的Java版本中删除)使用这些参数,我可以减少GC暂停时间,从0.4s到3s之间变化,一直小于0.2秒。为了完整起见,这些标志对于调试最有用:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution
请不要忘记,这些都针对非常具体的要求进行了优化:Onle one core available,很多年轻垃圾,没有旧收藏,非常低的世界停留
这是一个我可以推荐进一步阅读的小作弊表:http://blog.ragozin.info/2016/10/hotspot-jvm-garbage-collection-options.html