新生代、幸存区与老年代的调优方法
1 新生区内存调优
(1) 新生代的特点
- 对象分配极其廉价:使用TLAB,即Thread Local allocation buffer(参考:浅析java中的TLAB - 简书 (jianshu.com)),避免了线程竞争,提高了内存分配的效率。
- 对象的销毁代价小:采用复制算法整理内存,对于垃圾对象销毁代价小。
- 大部分对象朝生夕死,minor gc时间远低于full gc
由于新生代具有以上特点,对于新生代进行内存调优效果更明显,往往进行内存调优时先考虑新生代的内存调优。
(2) 设置新生代内存大小
参数-Xmn
可以设置新生代大小。如果新生代设置过小,将会导致minor gc频繁发生,耗费stw时间。如果新生代设置过大,或将导致只发生Full GC,占用的时间同样会很高,Oracle官方推荐设置为25%-50%。根据经验,我们一般将新生代的内存空间设置为:所容纳的最大并发量 * 一次请求响应的数据量。这样一次请求响应完成后大部分的内存将可以被释放,可以有效的减少GC的触发次数。
2 幸存区调优
(1) 设置幸存区大小
幸存区要至少能够存放当前活跃(将被回收)的对象+即将晋升(不被回收)的对象,如果幸存区的对象容纳不下,当前活跃的对象可能会被晋升到老年代,这就使一个本来拥有较短生命周期的对象在Full GC时才会被垃圾回收。
(2)设置合理晋升阈值
通过参数``-XX:+PrintTenuringDistribution可以打印各个年龄的对象在内存中的占用,
-XX:+MaxTenuringThreshold=threshold`调整晋升阈值。如果幸存区的晋升阈值设置过大,则需要晋升到老年代的对象可能不会被及时晋升。而新生代进行Minor GC时耗费时间主要发生在复制对象上,这就会导致STW时间变长。
######3 老年代调优
以CMS为例。
CMS的老年代的内存要尽可能大,避免浮动垃圾又导致内存溢出,使老年代退化。一般先进行新生代调优,有必要再考虑老年代调优。如果没有发生Full GC,一般无需对老年代进行调优,如果发生了Full GC,可以观察发生Full GC时老年代的内存占用超过的阈值,将老年代内存大小调大1/4~1/3.另外也可以用-XX:+CMSInitiatingOccupyFraction=percent
设置老年代垃圾回收的时机,一般推荐设置为内存占用75%~80%时。