JVM系列5-JVM调优

1、JVM参数设置

1.1 设置堆内存大小

  • 参数:**-Xmx:最大堆
       -Xms:最小堆**
  • 含义:堆大小指的是新生代和老生代大小之和。
  • 备注:Java应用程序运行时,首先会被分配-Xms指定的内存大小,并且尽可能尝试在这个空间段内运行。当内存无法满足程序时,JVM会向系统申请内存,直到内存大小为-Xmx。如果-Xms指定的内存较小,那么JVM GC的频率要更高。
  • 注意:当内存使用量到达-Xms时,会触发Full GC。因此,将-Xms-Xmx设置相等时,会减少系统初期GC次数和耗时。

1.2 设置新生代

  • 参数:**-XX:NewSize 新生代初始大小
       -XX:MaxNewSize新生代最大值
       -Xmn 新生代大小**,设置此值等于设置相同的-XX:NewSize 和-XX:MaxNewSize
  • 备注:设置不同的-XX:NewSize 和-XX:MaxNewSize可能会导致内存震荡。

1.3 设置持久代

  • 参数:**-XX:PermSize 持久代初始大小
       -XX:MaxPermSize持久代最大值**
  • 含义:持久代(方法区)不属于堆的一部分。持久代的大小直接决定了可以支持多少个类和多少个变量

1.4 设置线程栈

  • 参数:-Xss 线程栈大小
  • 含义:在线程中进行局部变量分配,以及函数调用时,都需要在栈中开辟空间。若栈的空间太小,则线程在执行时,或许没有足够的空间分配变量或达不到足够的函数调用深度,导致应用异常退出。若栈的空间太大,开启线程所需的成本会上升,系统所支持的线程数则会减少。
  • 备注:如果指定的最大堆的大小,则系统支持最大线程数,与堆的大小有关。如果分配给堆的内存过大,则系统支持的最大线程数则会减小。

1.5 堆的比例分配

  • 参数:-XX:SurvivorRatio 用来设置新生代中Eden区和幸存区(From)的比例
        * -XX:XX:SurvivorRatio = Eden/From = Eden/To*
       -XX:NewRatio设置老年代和新生代的比例
  • 含义:-XX:SurvivorRatio默认为8,新生代三部分空间比例8:1:1。-XX:NewRatio默认为2,新生代和老年代比例为1:2。

2、常见调优案例

2.1 将新对象留在新生代

Full GC的成本要远远高于Minor GC,所以尽可能将对象分配在新生代。虽然大多数情况下,JVM会尝试在Eden区分配新对象,由于内存紧张问题,可能不得不将年轻代对象提前向老年代压缩。所以,为应用程序分配一个恰当的新生代大小,可最大限度避免新对象直接进入老年代。一般的,当Survivor空间不够或占用量达到50%时,就会将对象进入老年代,可以通过调整参数-XX:TargetSurvivorRatio提高Survivor区的利用率。

以下代码申请了4MB空间:

public class Eden {
    public static void main(String[] args) {
        int[] a, b;
        a = new int[512 * 1024];  // 2MB
        b = new int[512 * 1024];  // 2MB
    }
}

使用JVM参数:-XX:+PrintGCDetails -Xms15M -Xmx15M运行,堆内存输出为:

Heap
 PSYoungGen      total 4608K, used 3325K [0x00000000ffa80000, 0x00000000fff80000, 0x0000000100000000)
  eden space 4096K, 81% used [0x00000000ffa80000,0x00000000ffdbf548,0x00000000ffe80000)
  from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
  to   space 512K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x00000000fff00000)
 ParOldGen       total 10240K, used 2048K [0x00000000ff000000, 0x00000000ffa00000, 0x00000000ffa80000)
  object space 10240K, 20% used [0x00000000ff000000,0x00000000ff200010,0x00000000ffa00000)
 PSPermGen       total 21504K, used 2892K [0x00000000f9e00000, 0x00000000fb300000, 0x00000000ff000000)
  object space 21504K, 13% used [0x00000000f9e00000,0x00000000fa0d3060,0x00000000fb300000)

可以看出,一个新对象已经进入了老年代。
使用参数:-XX:+PrintGCDetails -Xms15M -Xmx15M -Xmn8M运行,分配足够大的新生代,GC输出如下:

Heap
 PSYoungGen      total 7168K, used 5506K [0x00000000ff800000, 0x0000000100000000, 0x0000000100000000)
  eden space 6144K, 89% used [0x00000000ff800000,0x00000000ffd60850,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 4096K, used 0K [0x00000000ff200000, 0x00000000ff600000, 0x00000000ff800000)
  object space 4096K, 0% used [0x00000000ff200000,0x00000000ff200000,0x00000000ff600000)
 PSPermGen       total 21504K, used 2944K [0x00000000fa000000, 0x00000000fb500000, 0x00000000ff200000)
  object space 21504K, 13% used [0x00000000fa000000,0x00000000fa2e0150,0x00000000fb500000)

2.2 大对象进入老年代

大对象直接出现在新生代可能会扰乱新生代GC,这是因为在新生代尝试分配大对象,可能会导致空间不足,为了能容纳大对象,JVM不得不将新生代中其他年轻的对象移到老年代(或许会移动大量小的年轻对象进人老年代)。

综上,可以将大对象直接分配到老年代,但是许多大对象同时又是非活跃的对象,会对老年代GC产生问题,所以应尽量避免这种情况的发生。

以下代码申请了2MB空间:

public class Eden {
    public static void main(String[] args) {
        int[] a = new int[512 * 1024];  // 2MB
    }
}

由于-XX:PretenureSizeThreshold参数对PS收集器不适用,因此采用ParNew收集器。
使用JVM参数:-XX:+UseParNewGC -XX:+PrintGCDetails -Xms15M -Xmx15M 运行,堆内存输出为:

Heap
 par new generation   total 4608K, used 3481K [0x00000000f9e00000, 0x00000000fa300000, 0x00000000fa350000)
  eden space 4096K,  85% used [0x00000000f9e00000, 0x00000000fa1666e8, 0x00000000fa200000)
  from space 512K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa280000)
  to   space 512K,   0% used [0x00000000fa280000, 0x00000000fa280000, 0x00000000fa300000)
 tenured generation   total 10240K, used 0K [0x00000000fa350000, 0x00000000fad50000, 0x00000000fae00000)
   the space 10240K,   0% used [0x00000000fa350000, 0x00000000fa350000, 0x00000000fa350200, 0x00000000fad50000)
 compacting perm gen  total 21248K, used 2942K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  13% used [0x00000000fae00000, 0x00000000fb0df9c0, 0x00000000fb0dfa00, 0x00000000fc2c0000)
No shared spaces configured.

可以看到,这个对象基本上占据了整个新生代。
使用参数:-XX:+PrintGCDetails -Xms15M -Xmx15M -XX:PretenureSizeThreshold=1M运行,分配足够大的新生代,GC输出如下:

Heap
 par new generation   total 4608K, used 1353K [0x00000000f9e00000, 0x00000000fa300000, 0x00000000fa350000)
  eden space 4096K,  33% used [0x00000000f9e00000, 0x00000000f9f52560, 0x00000000fa200000)
  from space 512K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa280000)
  to   space 512K,   0% used [0x00000000fa280000, 0x00000000fa280000, 0x00000000fa300000)
 tenured generation   total 10240K, used 2048K [0x00000000fa350000, 0x00000000fad50000, 0x00000000fae00000)
   the space 10240K,  20% used [0x00000000fa350000, 0x00000000fa550010, 0x00000000fa550200, 0x00000000fad50000)
 compacting perm gen  total 21248K, used 2942K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  13% used [0x00000000fae00000, 0x00000000fb0df9c0, 0x00000000fb0dfa00, 0x00000000fc2c0000)
No shared spaces configured.

可以看到,新生代空间有很大的空闲,数组a已经被分配到了老年代上。

2.3 设置对象进入老年代年龄

可以通过-XX:MaxTenuringThreshold设置对象进入老年代的年龄。若对象在eden区,经过一次GC后,会被移动到幸存区,相应的年龄+1。当对象达到阈值(默认15)后,就会移动到老年代。

以下代码申请了3MB空间:

public class Eden {
    public static void main(String[] args) {
        byte[] a, b, c;
        a = new byte[1024 * 1024];  // 1MB
        b = new byte[1024 * 1024];  // 1MB
        c = new byte[1024 * 1024];  // 1MB
    }
}

使用JVM参数:
-XX:+UseParNewGC -XX:+PrintGCDetails -Xms15M -Xmx15M -XX:SurvivorRatio=3 运行,堆内存输出为:

[GC[ParNew: 2178K->644K(4096K), 0.0034094 secs] 2178K->1668K(14336K), 0.0034551 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
Heap
 par new generation   total 4096K, used 2922K [0x00000000f9e00000, 0x00000000fa300000, 0x00000000fa350000)
  eden space 3072K,  74% used [0x00000000f9e00000, 0x00000000fa039488, 0x00000000fa100000)
  from space 1024K,  62% used [0x00000000fa200000, 0x00000000fa2a13a0, 0x00000000fa300000)
  to   space 1024K,   0% used [0x00000000fa100000, 0x00000000fa100000, 0x00000000fa200000)
 tenured generation   total 10240K, used 1024K [0x00000000fa350000, 0x00000000fad50000, 0x00000000fae00000)
   the space 10240K,  10% used [0x00000000fa350000, 0x00000000fa450010, 0x00000000fa450200, 0x00000000fad50000)
 compacting perm gen  total 21248K, used 2890K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  13% used [0x00000000fae00000, 0x00000000fb0d2910, 0x00000000fb0d2a00, 0x00000000fc2c0000)
No shared spaces configured.

使用参数:-XX:+UseParNewGC -XX:+PrintGCDetails -Xms15M -Xmx15M -XX:SurvivorRatio=3
-XX:MaxTenuringThreshold=0运行,堆内存输出为:

[GC[ParNew: 2302K->0K(4096K), 0.0026039 secs] 2302K->1665K(14336K), 0.0026645 secs] [Times: user=0.06 sys=0.00, real=0.00 secs] 
Heap
 par new generation   total 4160K, used 2156K [0x00000000f9e00000, 0x00000000fa310000, 0x00000000fa350000)
  eden space 3136K,  68% used [0x00000000f9e00000, 0x00000000fa01b210, 0x00000000fa110000)
  from space 1024K,   0% used [0x00000000fa110000, 0x00000000fa110000, 0x00000000fa210000)
  to   space 1024K,   0% used [0x00000000fa210000, 0x00000000fa210000, 0x00000000fa310000)
 tenured generation   total 10240K, used 1665K [0x00000000fa350000, 0x00000000fad50000, 0x00000000fae00000)
   the space 10240K,  16% used [0x00000000fa350000, 0x00000000fa4f0500, 0x00000000fa4f0600, 0x00000000fad50000)
 compacting perm gen  total 21248K, used 2942K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  13% used [0x00000000fae00000, 0x00000000fb0dfa00, 0x00000000fb0dfa00, 0x00000000fc2c0000)
No shared spaces configured.

2.4 稳定与震荡的堆大小

稳定的堆大小可以减少GC次数,但也会增加每次GC的时间。可以让堆大小在一个区间内震荡,不需要使用大内存时,压缩堆指针,加快GC速度。
-XX:MinHeapFreeRatio:设置堆空间最小空闲比例,默认为40。当堆空间小于此值时,JVM会扩展堆空间。
-XX:MaxHeapFreeRatio:设置堆空间最大空闲比例,默认为70。当堆空间空闲内存大于此值时,JVM会压缩堆空间。
备注:当-Xms和-Xmx相等时,以上两个参数设置无效。

2.5 吞吐量优先

-Xms4G -Xmx4G -Xmn2G -Xss128k -XX:UseParallelGC -XX:ParallelGCThreads=20

2.6 降低停顿案例

-Xms4G -Xmx4G -Xmn2G -Xss128k -XX:UseParNewGC -XX:ConMarkSweepGC
-XX:ParallelGCThreads=20 -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31
目的是,最大限度将对象留在新生代(Minor GC成本远小于Full GC)。

3、调优总结

3.1 响应时间优先

  • 对于年轻代应尽可能设置大,极端情况下,年轻代GC的频率也是最小的。
  • 对于老年代要减少对象。老年代采用并发收集器。堆大小设置要尽量平衡,若太小,会造成内存碎片、高GC频率等,若太大,需要较长的GC回收时间。

3.2 吞吐量优先

  • 对于年轻代应尽可能设置大,对响应时间没有特别邀请,垃圾收集可并行进行。
  • 对于老年代设置要小,这样可以尽可能回收掉大部分短期对象,减少中期对象,而老年代存放长期对象。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值