面试

JVM系列5-JVM调优

2017-03-06  本文已影响64人  唐影若凡

声明:原创文章,转载请注明出处。http://www.jianshu.com/u/e02df63eaa87

1、JVM参数设置

1.1 设置堆内存大小
1.2 设置新生代
1.3 设置持久代
1.4 设置线程栈
1.5 堆的比例分配

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 响应时间优先
3.2 吞吐量优先
上一篇下一篇

猜你喜欢

热点阅读