一、JVM参数调优配置

版权声明:转载请注明出处 https://blog.csdn.net/chenmingxu438521/article/details/90579011

一、背景

1.Java虚拟机原理:所谓虚拟机,就是一台虚拟的机器,它是一款软件,用来执行一系列虚拟计算指令,大体上虚拟机可以分为系统虚拟机和程序虚拟机,大名鼎鼎的Visual Box、Vmware就属于系统虚拟机,他们完全是对物理计算的仿真,提供了一个可以运行完整操作系统的软件平台。程序虚拟机典型代表就是java虚拟机,它专门为执行单个计算程序而计算,在java虚拟机中执行的指令使我们程序成为java字节码,无论是系统虚拟机还是程序虚拟机,在上面运行的软件都被限制于虚拟机提供的资源中。

2.首先我们要对JVM参数进行调优,那么我们就得明白java内存结构,这样我们才能更进一步的去了解JVM参数进行调优(主要调优堆和垃圾回收机制,见下图)。

二、Java内存结构(JVM虚拟机存储空间)

1.首先我们要清楚java内存模型(多线程JMM)与Java内存结构的不同(JVM虚拟机存储空间)。

2.图解(自己画的)

2.1.class文件被类加载器classloader(采用的双亲委派模式)进行加载到内存空间中去。

2.2.什么是方法区:方法区又叫永久区,存放static关键字修饰的,常量信息当然包括字符串常量信息,方法区是什么时候被执行的?当class文件被加载的时候,就会初始化,所有线程会被共享(static修饰的线程会共享的)。

2.3.调优问题:web开发的时候,定义静态常量太多好不好,不好,因为定义太多的常量它是放在方法区的,垃圾回收机制对方法区不进行回收,所以内存会越来越大,很消耗内存。

扫描二维码关注公众号,回复: 6462035 查看本文章

2.4.堆:创建对象、new创建数组都是存放在堆内存中的,调优主要也是对它进行调优的,所有线程是会被共享的。

2.5.栈:定义基本局部变量,也存类的方法,栈代码运行完毕,自动释放内存,每个线程私有,互不共享,栈不会产生线程安全问题。

2.6.本地方法栈:主要调用底层C语言的(了解)。

三、详细讲堆内存

1.图解

1.1.什么是堆内存:创建对象、new创建数组都是存放在堆内存中的。

1.2.堆内存中又分为两个区:新生代、老年代,为啥这么分,主要是为了垃圾回收机制。

1.3.新生代又分为三个区:eden、s0、s1。

1.4.新生代:刚创建的对象,先放在新生代的eden区,比如user对象新建,先放在eden区,如果这个对象被频繁的使用,就会晋升到s0或者是s1区(s0、s1目的是为了垃圾回收机制的复制算法),如果在s0或者s1区中还是在频繁的使用,这时候就会晋升到老年代中。

1.5.老年代:如果对象在频繁的使用,对象放入到老年代。

1.6.分代的目的就是为了垃圾回收机制,垃圾回收机制主要回收新生代的对象,老年代也会回收不过次数很少的,还有一点就是老年代的对象不会逆流到新生代中去。

1.7.调优:在web系统中,尽量减少垃圾回收机制的次数,因为如果频繁的话,服务的其他线程都会被卡死,很影响效率的,所以不要频繁回收垃圾回收。

还有一个web开发的时候,定义静态常量太多好不好,不好,因为定义太多的常量它是放在方法区的,垃圾回收机制对方法区不进行回收,所以内存会越来越大,很消耗内存。

新生代回收次数要比老年代次数多。

四、堆内存参数配置

1.什么是虚拟机参数配置

在虚拟机运行的过程中,如果可以跟踪系统的运行状态,那么对于问题的故障排查会有一定的帮助,为此,在虚拟机提供了一些跟踪系统状态的参数,使用给定的参数执行Java虚拟机,就可以在系统运行时打印相关日志,用于分析实际问题。我们进行虚拟机参数配置,其实就是围绕着堆、栈、方法区、进行配置。你说下 你熟悉那些jvm参数调优。

2.堆的参数配置

-XX:+PrintGC      每次触发GC的时候打印相关日志

-XX:+UseSerialGC      串行回收

-XX:+PrintGCDetails  更详细的GC日志

-Xms               堆初始值

-Xmx               堆最大可用值

-Xmn               新生代堆最大可用值

-XX:SurvivorRatio     用来设置新代中eden空间和from/to空间的比例.

含以-XX:SurvivorRatio=eden/from=den/to

总结:在实际工作中,我们可以直接将初始的堆大小与最大堆大小相等,

这样的好处是可以减少程序运行时垃圾回收次数,从而提高效率。

-XX:SurvivorRatio     用来设置新代中eden空间和from/to空间的比例.

五、设置最大堆内存

1.代码栗子:

/**
 * Created by ChenMingXu on 2019/5/26.
 */
public class demo001 {

    public static void main(String[] args) throws InterruptedException {
        byte[] bytes01 = new byte[1 * 1024 * 1024];
        System.out.println("分配了1M内存");
        jvmInfo();
        Thread.sleep(3000);
        byte[] bytes02 = new byte[4 * 1024 * 1024];
        System.out.println("分配了4M内存");
        jvmInfo();
    }

    static private String toM(long maxMemory) {
        float num = (float) maxMemory / (1024 * 1024);
        DecimalFormat df = new DecimalFormat("0.00");// 格式化小数
        String s = df.format(num);// 返回的是String类型
        return s;
    }

    public static void jvmInfo() {
        // 最大内存配置信息kb
        long maxMemory = Runtime.getRuntime().maxMemory();
        System.out.println("maxMemory:" + maxMemory + "," + toM(maxMemory) + "M");
        // 当前空闲内存
        long freeMemory = Runtime.getRuntime().freeMemory();
        System.out.println("freeMemory:" + freeMemory + "," + toM(freeMemory) + "M");
        //已使用内存
        long totalMemory = Runtime.getRuntime().totalMemory();
        System.out.println("totalMemory:" + totalMemory + "," + toM(totalMemory) + "M");
    }
}

2.结果:

分配了1M内存
maxMemory:1879048192,1792.00M //默认堆内存大小
freeMemory:121798192,116.16M
totalMemory:126877696,121.00M
分配了4M内存
maxMemory:1879048192,1792.00M
freeMemory:117603872,112.16M
totalMemory:126877696,121.00M //可能因为底层打印太快了,具体再加上4M

3.调优参数

3.1.参数: -Xms5m -Xmx20m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintCommandLineFlags,// 堆初始值、堆最大可用值、串行回收、每次触发GC的时候打印相关日志,目的减少垃圾回收的次数已达到调优的目的。

3.2.怎么使用呢?

3.3.日志打印分析

3.3.1.初始值设置成5M、最大值设置成20M,这时候垃圾回收2次。

-XX:InitialHeapSize=5242880 -XX:MaxHeapSize=20971520 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC 
[GC (Allocation Failure) [DefNew: 1664K->192K(1856K), 0.0389905 secs] 1664K->671K(5952K), 0.1851062 secs] [Times: user=0.00 sys=0.00, real=0.18 secs] 
分配了1M内存
[GC (Allocation Failure) [DefNew: 1856K->106K(1856K), 0.0023211 secs] 2335K->1799K(5952K), 0.0023404 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
maxMemory:20316160,19.38M
freeMemory:3889352,3.71M
totalMemory:6094848,5.81M
[GC (Allocation Failure) [DefNew: 491K->134K(1856K), 0.0029878 secs][Tenured: 1797K->1931K(4096K), 0.0073899 secs] 2184K->1931K(5952K), [Metaspace: 3934K->3934K(1056768K)], 0.1023465 secs] [Times: user=0.02 sys=0.00, real=0.10 secs] 
分配了4M内存
maxMemory:20316160,19.38M
freeMemory:4153352,3.96M
totalMemory:10358784,9.88M
Heap
 def new generation   total 1920K, used 49K [0x00000000fec00000, 0x00000000fee10000, 0x00000000ff2a0000)
  eden space 1728K,   2% used [0x00000000fec00000, 0x00000000fec0c6a8, 0x00000000fedb0000)
  from space 192K,   0% used [0x00000000fedb0000, 0x00000000fedb0000, 0x00000000fede0000)
  to   space 192K,   0% used [0x00000000fede0000, 0x00000000fede0000, 0x00000000fee10000)
 tenured generation   total 8196K, used 6027K [0x00000000ff2a0000, 0x00000000ffaa1000, 0x0000000100000000)
   the space 8196K,  73% used [0x00000000ff2a0000, 0x00000000ff882fe0, 0x00000000ff883000, 0x00000000ffaa1000)
 Metaspace       used 3941K, capacity 4646K, committed 4864K, reserved 1056768K
  class space    used 435K, capacity 462K, committed 512K, reserved 1048576K

3.3.2.初始值设置成20M、最大值设置成20M,这时候垃圾回收1次,这样能达到调优的效果。

-XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC 
分配了1M内存
maxMemory:20316160,19.38M
freeMemory:16335496,15.58M
totalMemory:20316160,19.38M
[GC (Allocation Failure) [DefNew: 3887K->640K(6144K), 0.0076749 secs] 3887K->1931K(19840K), 0.0077995 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
分配了4M内存
maxMemory:20316160,19.38M
freeMemory:14039648,13.39M
totalMemory:20316160,19.38M
Heap
 def new generation   total 6144K, used 4893K [0x00000000fec00000, 0x00000000ff2a0000, 0x00000000ff2a0000)
  eden space 5504K,  77% used [0x00000000fec00000, 0x00000000ff0274f8, 0x00000000ff160000)
  from space 640K, 100% used [0x00000000ff200000, 0x00000000ff2a0000, 0x00000000ff2a0000)
  to   space 640K,   0% used [0x00000000ff160000, 0x00000000ff160000, 0x00000000ff200000)
 tenured generation   total 13696K, used 1291K [0x00000000ff2a0000, 0x0000000100000000, 0x0000000100000000)
   the space 13696K,   9% used [0x00000000ff2a0000, 0x00000000ff3e2e48, 0x00000000ff3e3000, 0x0000000100000000)
 Metaspace       used 3942K, capacity 4646K, committed 4864K, reserved 1056768K
  class space    used 435K, capacity 462K, committed 512K, reserved 1048576K

六、配置新生代与老年代调优参数

1.设置新生代与老年代回收比例,jvm参数调优,怎么样让垃圾回收机制经常去新生代进行回收,新生代与老年代1:3或者1:4的比例进行分配

2.代码演示(设置新生代比例参数例子,配置新生代的eden与s0或者s1的比例关系

/**
 * Created by ChenMingXu on 2019/5/27.
 */
public class demo002 {
    public static void main(String[] args) {
        //调优参数
        //-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC //配置新生代的eden与s0或者s1的比例关系
        byte[] bytes = null;
        for (int i = 0; i < 10; i++) {
            System.out.println("i:" + i);
            bytes = new byte[1 * 1024 * 1024];
        }
    }
}

2.1.调优参数:-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

解释:-Xmn  新生代大小值,一般设为整个堆的1/3到1/4左右。

           -XX:SurvivorRatio    设置新生代中eden区和from/to(s0和s1区)空间的比例关系n/1

2.2.运行结果

[GC (Allocation Failure) [DefNew: 506K->256K(768K), 0.0008095 secs] 506K->430K(20224K), 0.0008442 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew: 768K->169K(768K), 0.0010041 secs] 942K->598K(20224K), 0.0010229 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew: 681K->58K(768K), 0.0005780 secs] 1110K->655K(20224K), 0.0005968 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
i:0
i:1
i:2
i:3
i:4
i:5
i:6
i:7
i:8
i:9
Heap
 def new generation   total 768K, used 514K [0x00000000fec00000, 0x00000000fed00000, 0x00000000fed00000)
  eden space 512K,  89% used [0x00000000fec00000, 0x00000000fec72008, 0x00000000fec80000)
  from space 256K,  22% used [0x00000000fecc0000, 0x00000000fecce978, 0x00000000fed00000)
  to   space 256K,   0% used [0x00000000fec80000, 0x00000000fec80000, 0x00000000fecc0000)
 tenured generation   total 19456K, used 10837K [0x00000000fed00000, 0x0000000100000000, 0x0000000100000000)
   the space 19456K,  55% used [0x00000000fed00000, 0x00000000ff795490, 0x00000000ff795600, 0x0000000100000000)
 Metaspace       used 3443K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 374K, capacity 388K, committed 512K, reserved 1048576K

2.3.运行结果分析

此时新生代中eden区和from/to(s0和s1区)空间的比例关系2:1,即:(256+256)=512k

3.代码演示(设置新生代与老年代比例参数例子

/**
 * Created by ChenMingXu on 2019/5/27.
 */
public class demo002 {
    public static void main(String[] args) {
        //调优参数
        //-Xms20m -Xmx20m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC -XX:NewRatio=2 //配置新生代与老年代的关系
        byte[] bytes = null;
        for (int i = 0; i < 10; i++) {
            System.out.println("i:" + i);
            bytes = new byte[1 * 1024 * 1024];
        }
    }
}

3.1.调优参数:-Xms20m -Xmx20m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC -XX:NewRatio=2 //配置新生代与老年代的关系(1:2),在实际中尽量设置(1:3或者1:4).

总结:不同的堆分布情况,对系统执行会产生一定的影响,在实际工作中,应该根据系统的特点做出合理的配置,基本策略:尽可能将对象预留在新生代,减少老年代的GC次数。除了可以设置新生代的绝对大小(-Xmn),可以使用(-XX:NewRatio)设置新生代和老年代的比例:-XX:NewRatio=老年代/新生代

3.2.运行结果

i:0
i:1
[GC (Allocation Failure) [DefNew: 3095K->1664K(5120K), 0.0027022 secs] 3095K->1744K(18816K), 0.0027460 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
i:2
i:3
i:4
[GC (Allocation Failure) [DefNew: 4800K->1024K(5120K), 0.0019211 secs] 4880K->1744K(18816K), 0.0019513 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
i:5
i:6
i:7
[GC (Allocation Failure) [DefNew: 4161K->1024K(5120K), 0.0007788 secs] 4881K->1744K(18816K), 0.0008016 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
i:8
i:9
Heap
 def new generation   total 5120K, used 4231K [0x00000000fec00000, 0x00000000ff2a0000, 0x00000000ff2a0000)
  eden space 3456K,  92% used [0x00000000fec00000, 0x00000000fef21f98, 0x00000000fef60000)
  from space 1664K,  61% used [0x00000000ff100000, 0x00000000ff200010, 0x00000000ff2a0000)
  to   space 1664K,   0% used [0x00000000fef60000, 0x00000000fef60000, 0x00000000ff100000)
 tenured generation   total 13696K, used 720K [0x00000000ff2a0000, 0x0000000100000000, 0x0000000100000000)
   the space 13696K,   5% used [0x00000000ff2a0000, 0x00000000ff354040, 0x00000000ff354200, 0x0000000100000000)
 Metaspace       used 3440K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 374K, capacity 388K, committed 512K, reserved 1048576K

3.3.运行结果分析:新生代:老年代=1:2、(3456+1664+1664)x2=13568k,约等于老年代的内存大小13696k

七、堆溢出解决方法

1.代码例子

/**
 * Created by ChenMingXu on 2019/5/27.
 */
public class demo003 {
    public static void main(String[] args) {
        List<Object> listObject = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            System.out.println("i:"+i);
            listObject.add(new byte[1*1024*1024]);
        }
        System.out.println("创建完毕!");
    }
}

2.参数设置:-Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError(因为listObject也要占内存,所以会报堆内存溢出):

i:0
i:1
i:2
i:3
i:4
i:5
i:6
i:7
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid14788.hprof ...
Heap dump file created [8977694 bytes in 0.045 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at demo003.main(demo003.java:12)

3.解决办法参数设置:-Xms10m -Xmx40m -XX:+HeapDumpOnOutOfMemoryError(实际也是修改-Xmx的大小一般为30m或者40m)。

3.1.打印结果

i:0
i:1
i:2
i:3
i:4
i:5
i:6
i:7
i:8
i:9
创建完毕!

八、栈溢出解决方法

1.代码演示

/**
 * Created by ChenMingXu on 2019/5/27.
 */
//什么是栈溢出? 无线递归调用
public class demo004 {
    private static int count = 0;

    public static void getCount() {
        try {
            count++;
            getCount();
        } catch (  Throwable e) {
            System.out.println("最大的深度...." + count);
            e.printStackTrace();
        }

    }
    //栈溢出 是方法中递归调用 不是循环调用方法 发生栈溢出
    public static void main(String[] args) {
//		for (int i = 0; i < 10000; i++) {
        getCount();
    }
}

2.结果显示(栈溢出了错误)

java.lang.StackOverflowError
最大的深度....13483
	at java.util.HashMap.afterNodeAccess(HashMap.java:1779)
	at java.util.HashMap.putVal(HashMap.java:657)
	at java.util.HashMap.put(HashMap.java:612)
	at java.util.HashSet.add(HashSet.java:220)
	at java.util.Collections$SynchronizedCollection.add(Collections.java:2035)
	at java.lang.ClassLoader.checkPackageAccess(ClassLoader.java:508)
	at demo004.getCount(demo004.java:11)
	at demo004.getCount(demo004.java:11)
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 876

3.解决办法

3.1.栈溢出产生于递归调用,循环遍历是不会的,但是循环方法里面产生递归调用, 也会发生栈溢出。

3.2.解决办法:设置线程最大调用深度,-Xss5m 设置最大调用深度

3.3.打印结果(最大深度比上面的大了很多了)

java.lang.StackOverflowError
最大的深度....274094
	at java.util.HashMap.afterNodeAccess(HashMap.java:1779)
	at java.util.HashMap.putVal(HashMap.java:657)
	at java.util.HashMap.put(HashMap.java:612)
	at java.util.HashSet.add(HashSet.java:220)
	at java.util.Collections$SynchronizedCollection.add(Collections.java:2035)
	at java.lang.ClassLoader.checkPackageAccess(ClassLoader.java:508)
	at demo004.getCount(demo004.java:11)
	at demo004.getCount(demo004.java:11)
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 876

九、结束

以上就是我对jvm调优的学习以及认识,如有哪里做的不好,请批评指正,共勉!!!

Always keep the faith!!!

猜你喜欢

转载自blog.csdn.net/chenmingxu438521/article/details/90579011