新生代、幸存区与老年代的调优方法

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%时。