JVM之优化延迟或响应时间
优化标准
在优化延迟的时候,其实就是要调整堆大小,年轻代大小,让GC的时间和频率达到要求。下面是几个优化时需要参考的标准:
- Minor GC的时间
- Minor GC的频率
- Full GC的时间
- Full GC的频率
优化年轻代大小
当测试垃圾回收数据的时候,发现MinorGC的时间太长了,正确的做法就是减少young代的空间大小。如果MinorGC太频繁了就增加young代的空间大小。
看看下面例子是怎么优化年轻代:
上图是一个展示了MinorGC的例子,这个例子是运行在如下的HotSpot VM命令参数下的。
-Xms6144m -Xmx6144m -Xmn2048m -XX:PermSize=96m -XX:MaxPermSize=96m -XX:+UserParallelOldGC
上图显示了MinorGC平均的消耗时间是0.05秒,平均的频率是2.147秒1次。当计算MinorGC的消耗时间和频率的时候,越多的数据参与计算,准确性会越高。并且应用要处于稳定运行状态下来收集MinorGC信息也是非常重要的。
- 如果要求消耗时间是0.04秒,那应该调小年轻代
- 如果要求频率是5秒一次,就应该调大年轻代
注意
- 调整年轻代大小时,要控制老年代不变,所以需要同时调整-Xms和-Xmx
- 增大或减少堆空间时,按10%去调整
- old代的空间一定不能小于活动对象的大小的1.5倍
- young代的空间至少要有Java堆大小的10%,太小的Java空间会导致过于频繁的MinorGC
- 当提高Java堆大小的时候,不要超过JVM可以使用的物理内存大小。如果使用过多的物理内存,会导致使用交换区,这个会严重影响性能
如果在仅仅是MinorGC导致了延迟的情况下,你无法通过调整young代的空间来满足系统的需求,那么你需要重 新修改应用程序、修改JVM部署模型把应用部署到多个JVM上面(通常得要多机器了)或者重新评估系统的需求。
计算老年代垃圾回收的频率
上面的Minor GC,可以通过日志就能看到GC的频率和时间,但是对于Full GC,也是可以通过日志来看到Full GC 的时间和频率。其实频率也是可以估算出来的。
接下有个几个MinorGC的例子,他们被用来评估FullGC的频率。
2010-12-05T14:40:29.564-0800: [GC
[PSYoungGen: 2045989K->249795K(2097152K)]
3634533K->1838430K(6291456K), 0.0543798 secs]
[Times: user=0.38 sys=0.01, real=0.05 secs]
2010-12-05T14:40:31.949-0800: [GC
[PSYoungGen: 2047896K->247788K(2097152K)]
3655319K->1859216K(6291456K), 0.0539614 secs]
[Times: user=0.35 sys=0.01, real=0.05 secs]
2010-12-05T14:40:34.346-0800 [GC
[PSYoungGen: 2045889K->248993K(2097152K)]
3677202K->1881099K(6291456K), 0.0532377 secs]
[Times: user=0.39 sys=0.01, real=0.05 secs]
2010-12-05T14:40:36.815-0800 [GC
[PSYoungGen: 2047094K->247765K(2097152K)]
3696985K->1900882K(6291456K), 0.0543332 secs]
[Times: user=0.37 sys=0.01, real=0.05 secs]
从上面的例子可以看出:
1、Java堆的大小是6291456K或6144M
2、young代的大小是2097152K或2048M
3、old代的大小是6144M-2048M = 4096M
在这个例子中,活动对象的大小差不多是1370M。那么old代还有2726M剩余空间(4096M-1370M=2726M)。
填充完成2736M空间需要多长时间是由young代向old代的转移率决定的。这个转移率的计算通过查看每次MinorGC后old代的占用空间的增长情况以及MinorGC发生的时间。old代的空间占用是MinorGC之后Java堆中对象大小减去young代的大小,通过这个公式计算,可以看出在这个例子中每次MinorGC之后,old代的空间占用情况是:
1588635K,第一个MinorGC
1611428K,第二次MinorGC
1632106K,第三次MinorGC
1653117K,第四次MinorGC
每次的增量分别是
22793K,第一次和第二次的增量
20678K,第二次和第三次的增量
21011K,第三次和第四次的增量
平均每次MinorGC转移大概201494K或者叫21M。
如果剩余的空间都是按照设个转移率来转移到old代的话,且知道MinorGC的频率是每2.147秒一次。因此,这个转移率是201494K/2.147s差不多10M/s,那么一共的空间是2736M空间需要273.6s差不多4.5分钟一次。
因此,通过前面的案例分析,应用的最差延迟的频率是4.5分钟。这个评估可以通过让应用处于稳定运行状态超过4.5分钟来验证。
优化老年代大小
如果评估和观察的FullGC的频率高于了应用对最坏延迟频率的要求,那么可以提高old代的空间大小。如果改变old代的大小,保持young代的空间恒定,在优化young代的时候也说这个问题,两者应该独立优化,以保证有高效。
Full GC 例子:
如果这步已经达到了你最坏延迟的要求,那么这一步调优延迟就算已经完成了,就可以进入下一步去调优“吞吐量”了。
如果你未能达到了应用对最坏延迟时间和频率的性能要求,由于FullGC的执行时间太长了,然后你可以把垃圾回收器切换CMS(concurrent garbage collection)。CMS有能力让垃圾回收尽量是多线程的,即让程序保持在运行状态。要使用CMS可以通过下面这条命令选项:-XX:+UseConcMarkSweepGC。