闭关修炼(十五)JVM参数调优


Java虚拟机

什么是java虚拟机

所谓虚拟机,就是一台虚拟的机器,它是一款软件,用来执行一系列虚拟计算指令,他们完全是对物理计算的仿真,提供了一个可以运行完整操作系统的平台。

java虚拟机,它专门为执行单个计算程序而计算,在java虚拟机中执行的指令我们称为java字节码指令。

在虚拟机上运行的软件都限制于虚拟机提供的资源中。

Java内存结构

什么是java内存结构

JVM虚拟机存储空间。

java文件编译后变为class文件,classLoader类加载器读取class文件,分配资源到哪个内存空间去。

内存空间:

  1. 方法区
    方法区又称为永久区,static关键字修饰的存放在方法区,方法区一般存放常量信息,当class文件被加载的时候,方法区就会被初始化,方法区是全局的。


  2. 创建(new)的对象,数组等等,存放在堆内存中。堆内存所有线程都会共享,


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

  4. 本地方法栈
    主要作用是调用C语言,安卓开发中,底层可能会用C语言,java去使用C语言用到JNI技术,会用到本地方法栈。

PC寄存器:
每个线程都有PC寄存器,每个线程的PC寄存器是私有的。执行命令的指针

执行引擎:
负责执行字节码文件

调优策略主要是堆和垃圾回收机制。

堆内存

new出来的对象都会存放在堆内存中,堆内存中分配两个区,分为新生代和老年代,分代作用是利于垃圾回收机制

新生代,垃圾回收机制不经常回收的区域。
老年代:如果对象被频繁的使用,将对象放入到老年代中。

新生代里又分为eden、s0、s1区。其中s0和s1区大小相等,目的是垃圾回收复制算法。其中s0、s1又称为from/to区

  • eden:刚创建的对象存放在eden区
  • s0、s1:在eden区的对象经常被使用后转移到s0或者s1区。
  • 老年代:如果s0或者s1区的对象仍在频繁使用就晋升到老年代区,到了老年代的对象不会回到新生代,是不可逆的转移过程。

垃圾回收机制主要回收新生代。

调优:尽量减少垃圾回收机制的次数。在web系统中尽量减少常量信息。尽量减少老年代的回收次数,提高新生代的回收次数。

堆内存参数配置

什么是虚拟机参数配置?

使用给定的参数执行java虚拟机,就可以在系统运行时打印相关日志,用于分析实际问题。

主要围绕堆、栈、方法区进行配置

堆的配置参数

参数说明
-XX:+PrintGC每次触发GC时打印相关日志
-XX:+UserSerialGC串行回收
-XX:+PrintGCDetails更详细的GC日志
-Xms堆初始值
-Xmx堆最大可用值
-Xmn新生代堆最大可用值
-XX:SurvivorRatio用来设置新生代中eden空间和trom/to空间的比例

实际工作中,我们一定要把初始的堆大小与最大堆大小相等,这样的好处是可以减少程序运行时垃圾回收次数,从而提高效率

显示堆参数例子

import java.text.DecimalFormat;

public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        // 新建byte数组
        byte[] bytes1 = new byte[1024 * 1024];
        System.out.println("分配1M内存");
        jvmInfo();
        Thread.sleep(5000);
        byte[] bytes2 = new byte[4 * 1024 * 1024];
        System.out.println("分配4M内存");
        jvmInfo();
    }

    /**
     * 比特转换为M
     *
     * @param bt 多少比特
     * @return {@link String} 多少M,保留两位小数
     */
    static public String toM(long bt) {
        float num = (float) bt / (1024 * 1024);
        // 设置小数格式
        DecimalFormat df = new DecimalFormat("0.00");
        return df.format(num);
    }

    /**
     * 显示jvm信息
     */
    static public void jvmInfo() {
        // 最大内存配置信息 单位K
        long maxMemory = Runtime.getRuntime().maxMemory();
        System.out.println("最大内存:" + toM(maxMemory) + " M");
        // 当前空闲内存
        long freeMemory = Runtime.getRuntime().freeMemory();
        System.out.println("当前空闲内存:" + toM(freeMemory) + " M");
        // 返回Java虚拟机中的内存总量
        long totalMemory = Runtime.getRuntime().totalMemory();
        System.out.println("内存总量:" + toM(totalMemory) + " M");
        // 返回Java虚拟机可用的处理器数量
        long availableProcessors = Runtime.getRuntime().availableProcessors();
        System.out.println("可用的处理器数量:" + availableProcessors);
    }
}


totalMemory()应该是已用内存的意思
在这里插入图片描述

配置堆参数-初始值和最大值

Edit Configurations

在这里插入图片描述
VM arguments添加参数
-Xms5m -Xmx20m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintCommandLineFlags
在这里插入图片描述

运行结果
在这里插入图片描述
这一条日志表示GC已经回收了

[GC (Allocation Failure) [DefNew: 1664K->192K(1856K), 0.0015332 secs] 1664K->626K(5952K), 0.0211005 secs] [Times: user=0.00 sys=0.00, real=0.02 secs] 

可以看到GC回收了两次

在这里插入图片描述
我们把参数 -Xms5m -Xmx20m改成 -Xms20m -Xmx20m,初始值和最大值相等,看看结果,就只回收了一次
在这里插入图片描述

为什么初始值越小,垃圾回收机制次数越多?
初始值越小,当要分配较大的内存时,空间不够,分配内存的时候就越需要进行回收

配置堆参数-设置新生代参数

-Xmn 新生代大小
-XX:SurvivorRatio 设置新生代中eden区与from/to空间的比例

VM arguments添加参数
-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

设置新生代大小为2m,eden与from/to比例为2:1

public class Test2 {
    public static void main(String[] args) {
        byte[] bytes = null;
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
            bytes = new byte[1024*1024];
        }
    }
}

运行结果,eden与from/to比例为2:1
在这里插入图片描述

配置堆参数-设置新生代与老年代比例

-XX:NewRation 设置老年代/新生代的大小
基本策略:尽可能将对象留在新生代,减少老年代的GC次数。

VM arguments添加参数
-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC -XX:NewRatio=2

-XX:NewRatio=2,则Old Generation是 Yong Generation的2倍,即Yong Generation占据内存的1/3,垃圾回收机制主要回收新生代,尽量减少垃圾回收机制的次数,一般Yong Generation设成占据内存的1/3或者1/4

堆溢出解决办法

堆溢出问题模拟
VM arguments设置参数
-Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails

-XX:+HeapDumpOnOutOfMemoryError 打印问题溢出问题

创建10M的List

	public static void main(String[] args) {
        List<Object> list = new ArrayList<Object>();
        for (int i = 0; i < 10; i++) {
            list.add(new byte[1024 * 1024]);
        }
        System.out.println("创建完毕");
    }

在这里插入图片描述

加堆内存大小即可, -Xms30m -Xmx30m

Tomcat内存溢出在catalina.sh修改堆内存大小

在这里插入图片描述
JAVA_OPTS:(可选)在执行任何命令时使用的Java运行时选项。JAVA_OPTS中的所有选项应该被Tomcat、停止进程、版本命令等使用。

JAVA_OPTS="-server -Xms800m -Xmx800m -XXNewSize=256M -XX:PermSize=256M -XX:MaxNewSize=512m -XX:MaxPermSize=512m"

什么是栈溢出

操作变量时无限的递归进行调用会造成栈溢出,循环调用方法不会造成栈溢出

栈溢出解决方法

栈溢出问题模拟

public class Test4 {
    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();
    }
}

在这里插入图片描述

配置参数只能增加深度,而不能解决无限递归造成的栈溢出问题
-Xss5m 设置最大调用深度
在这里插入图片描述
从1万2千多到26万多
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值