JVM常用调优案例
1.将新对象预留在新生代
由于Full GC的成本远高于Minor GC,因此尽可能将对象分配在新生代是一项明智的做法。虽然在大部分情况下,JVM会尝试在eden区分配对象,但是由于空间紧张等问题,很可能不得不将部分年轻对象提前向老年代压缩。
在JVM参数调优中,可以为应用程序分配一个合理的新生代空间,以避免新对象直接进入老年代的情况。因为新生代垃圾回收的速度高于老年代回收。因此,将年轻对象预留在新生代有利于提升整体的GC效率。
2.大对象进入老年代
虽然在大部分情况下,将对象分配在新生代是合理的。但是,对于大对象,这种做法是值得商榷的。大对象出现在新生代很可能扰乱新生代GC,并破坏新生代原有的对象结构。
因为尝试在新生代分配大对象,很可能导致空间不足,为了有足够的空间容纳大对象,JVM不得不将新生代中的年轻对象挪到老年代。因为大对象占用空间多,所以可能需要移动大量的小的年轻对象进入老年代。因为新生代的对象往往存活时间比较短,所以会引起多次的Full GC造成程序卡顿。
基于以上的原因,可以直接将大对象分配到老年代,以保持新生代对象结构的完整性,提高GC的效率。
可以使用参数-XX:PretenureSizeThreshold设置大对象直接进入老年代的阈值。当对象的大小超过这个值,将直接在老年代分配。
3.设置对象进入老年代的年龄
在堆中,每个对象都有自己的年龄,一般情况下,年轻对象存放在新生区,年老对象存放在老年区。为了做到这一点,JVM会为每一个对象都维护一个年龄
如果对象在eden区,经过一次GC之后还存活,则被移动到survivor区中,对象年龄加1 。之后对象每经过一次GC之后依然存活,则年龄再加1 。当对象年龄到达阈值,就移入老年代,成为老年对象。
这个阈值的最大值可以通过参数-XX:MaxTenuringThreshold来设置,它的默认值为15.但这并不意味着新对象一定要达到这个年龄才能进入老年代。对象实际进入老年代的年龄是虚拟机在运行时根据内存使用情况动态计算的,这个参数指定的是阈值的最大值。即实际进入老年代的年龄等于动态计算所得的年龄与参数指定的中较小的那一个。
4.稳定与震荡的堆大小
一般来说,稳定的堆大小对垃圾回收是有利的。获得一个稳定的堆大小的方法是使-Xms和-xmx的大小一致,即最大堆和最小堆都一样。
如果这样设置,系统在运行时,堆的大小是恒定的,稳定的堆大小可以减少GC的次数,因此很多 服务端应用都会将最大堆和最小堆设置为相同的数值。
但是一个不稳定的堆大小也并非一无是处。稳定的堆大小虽然减少了GC的次数,但是同时也增加了GC的时间。让堆大小在一个区间中震荡,在系统不需要使用大内存时,压缩堆空间,使GC应对一个较小的堆可以加快单次GC的速度。
基于这样的考虑,JVM还提供了两个参数用于压缩和拓展堆空间:
- -XX:MinHeapFreeRatio : 设置堆空间的最小空闲比例,默认是40.当堆空间的空闲内存小于这个数值时,JVM会拓展堆空间。
- -XX:MinHeapFreeRatio : 设置堆空间的最大空闲比例,默认是70.当堆空间的空闲内存大于这个数值时,JVM会拓展空间。
当-XMS和-Xmx相等时,这两个参数是无效的。