java虚拟机(JVM)参数调优

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/CoderYin/article/details/82422583

                              java虚拟机(JVM)参数调优

介绍:参数调优主要调垃圾回收机制

 

一、java内存结构

1、内存结构

         a、方法区(永久区):static关键字修饰、常量信息当class文件被加载的时候,就会被初始化。所有线程会被共享。所以注意线程安全问题。

         b、堆:创建对象、new对象、数组存放在堆内存。堆内存所有线程共享。

         c、栈:局部变量,类方法。代码运行完毕,自动释放内存,栈每个线程私有互不共享,线程安全。

         d、本地方法栈:主要调用C语言,JNI

         e、PC寄存器

         f、执行引擎

         g、垃圾回收机制

2、图式结构

注意:java内存结构java内存模型区别。

Java内存模型是根据英文Java Memory Model(JMM)翻译过来的。其实JMM并不像JVM内存结构一样是真实存在的。他只是一个抽象的概念。JSR-133: Java Memory Model and Thread Specification中描述了,JMM是和多线程相关的,他描述了一组规则或规范,这个规范定义了一个线程对共享变量的写入时对另一个线程是可见的。

那么,简单总结下,Java的多线程之间是通过共享内存进行通信的,而由于采用共享内存进行通信,在通信过程中会存在一系列如可见性、原子性、顺序性等问题,而JMM就是围绕着多线程通信以及与其相关的一系列特性而建立的模型

二、堆内存(新生代和老年代)

1、概述:堆内存主要用来存放刚new出来的对象和数组,堆内存主要分为新生代区和老年代区(主要是为了垃圾回收机制)。新生代区又分为eden区、s0、s1区(s0和s1区的大小是相等的,只要是为了垃圾回收机制的复制算法)。刚创建的对象会存放在eden区频繁使用的话->s0区或者s1区->最后到老年代区。

2、新生代:刚创建的对象,先存放在新生代。

3、老年代:如果对象在频繁的使用,对象会由新生代区存放到老年代区。

4、垃圾回收机制回收新生代区的次数要比老年代频繁的多,因为老年代都是存在的频繁使用的对象,不需要频繁的回收内存,而新生代区存放的是刚创建的对象可能不需要频繁的的使用,如果不频繁的回收会消耗内存空间。

5、堆内存图式:

三、堆内存参数配置

1、介绍:堆的初始内存设置和对的最大内存一致,这样可以减少垃圾回收机制的执行次数。

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空间的比例.

 

3、案例介绍:

代码:

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;
	}

	private static void jvmInfo() {
		// 最大内存配置信息
		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");
	}

未配置堆内存参数结果

配置项目的堆内存参数

分配初始5M最大20M参数结果:

分配初始20M最大20M参数结果:

结论:可以明显发现,垃圾回收机制少执行了一次。所以堆内存中的初始化内存大小要和最大一致,这样可以减少垃圾回收机制执行,这样才会有利于jvm参数调优。当然如果你把初始和最大参数调到几百M,垃圾回收机制应该不会执行。

四、配置新生代和老年代调优参数

1、-Xmn    新生代大小,一般设为整个堆的1/3到1/4左右

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

3、-XX:NewRatio=2     老年代/新生代(下图(eden+form+to)*2==generate

参数: -Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

参数:-Xms20m -Xmx20m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC -XX:NewRatio=2

五、堆溢出解决方法

案例:

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("创建完毕");

解决方法:将-Xmx30m替换掉上面的。

六、栈溢出解决方法

1、介绍:方法在操作变量时,无限的递归调用。

2、案例:

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) {
		getCount();


    /*该处并不会发生栈溢出问题
        for(int i=0;i<100000;i++){
            getCount();
        }
    */
	}

运行结果:

3、解决方法:配置参数-Xss5m,可以提升最大深度。

Tomcat内存溢出catalina.sh 修改JVM堆内存大小
JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxNewSize=512m"

七、调优总结

1、在web系统中尽量减少常量信息定义。(不会被垃圾回收机制清除占用内存导致系统资源占用

2、尽量减少垃圾回收机制。(垃圾回收线程会导致其他线程卡顿,影响效率

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

猜你喜欢

转载自blog.csdn.net/CoderYin/article/details/82422583