JVM原理和JVM调优

1 篇文章 0 订阅

第一章:类加载机制

问题:
1、类加载总流程
2、每个步骤的目的
3、类加载器
3、双亲委派机制的目的和原因

一、类加载运行的全过程

    • 加载=》验证=》准备=》解析=》初始化
  • 1、加载:懒加载,使用到类的时候才会进行加载
  • 2、验证:校验字节码文件的正确性,文件头有coffee bebe
  • 3、准备:给类的静态变量分配内存,并给默认值
  • 4、解析:符号引用转化为直接引用,该阶段会把一些静态方法(符号引用,比如 main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过 程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用
  • 5、初始化:对类的静态变量赋值为指定的值;静态代码块开始执行
    在这里插入图片描述

二、类加载器和双亲委派机制

  • 类加载器分类(从上至下)

    • 引导类加载器:负责加载jre中lib下的核心类库
    • 扩展类加载器:负责加载jre下lib下ext扩展目录中的类
    • 应用类加载器:负责加载classPath下的,我们自己写类
    • 自定义加载器:用户自定义下的类包
  • 双亲委派机制

  • 在这里插入图片描述

  • 第一种我们自己编写一个Test类加载过程如下:

    • 系统刚启动时(所有类加载器什么都没有是空的,所以)
    • 首先由自定义加载器开始加载=》再到应用类加载器=》再到扩展类加载器=》再到引导类加载器(一直向上委托,但是顶层加载器都没有,然后再向下由其子加载器加载)
    • 再引导类。。=》扩展类。。=》应用类。。(到应用类加载器就可以开始加载了)
    • 下次再次使用时就直接由对应的类加载器加载就行
  • 使用双亲委派机制的原因

    • 1、沙箱安全机制:避免核心类库API被改动
    • 2、避免类的重复加载:若父加载器已经加载了那么子加载器就没必要加载了,保证类的唯一性

第二章:JVM内存模型于优化

问题:
1、主体流程
2、栈中结构
3、动态链接和静态链接
4、方法区(放什么)和常量池,和动态链接之间的关系
5、堆的结构,简单调优,gc概述
6、年轻代结构,老年代;年轻代和老年代之间的关系;移到老年代的两种情况(分代年龄,和年轻代放不下)
7、java自带的jvisualvm调优
8、只要gc都会stw,minor Gc和full GC之前的问题
9、为什么stw机制
10、普通的调优参数

一、java一处编译到处执行

只要有了JVM,那么有java.class那么就能执行
在这里插入图片描述

二、JVM内存模型

在这里插入图片描述

先由下面的程序串一下整个JVM内存模型的流程

package com.xusj.jvmInfo;

/**
 * @author xusj
 * <br>CreateDate 2022/6/14 11:32
 */
public class Test {
    // 静态常量
    private final static String myA = "a";

    // 普通方法,放在方法区中类信息
    public int getSth() {
        int a = 1;
        int b = 2;
        int c = a + b;
        return c;
    }

    public static void main(String[] args) {
        // new 对象
        Test test = new Test();
        // 调用对象中的普通方法,通过动态链接将符号引用转化为直接引用
        test.getSth();
    }
}

  • 1、主线程开始执行main方法

    • 1.1 给主线程分配一个栈
    • 1.2 将main方法压栈(这是一个栈帧,每个栈帧包含如下几个内容【局部变量、操作数栈、动态链接、方法出口】)
    • 1.3 主线程运行到new Test();就会在堆中创建一个对象,然后将堆中的内存地址赋值到局部变量中,该栈帧中的局部变量指向堆中的对象。
    • 1.4 调用该对象中的普通方法(通过栈中的动态链接将符号引用转化为直接引用【动态链接就是链接到方法区中的:常量+静态变量+类信息】)
    • 1.5 进行方法中的运算,先将a压到局部变量表中,再将1压到操作数栈,然后将a进行在操作数栈中赋值,最后将其放到局部变量表中
    • 1.6 在这个过程中,使用计数器去记录程序运行行号
    • 1.7 如果使用到本地方法,每个线程还有由一个本地方法栈
  • 2、运行时数据区主要的介绍(注意私有和公有的区别,做到心中有图)

    • 本地方法栈:每个线程私有,java底层native修饰的本地方法,使用c/c++;
    • 栈:每个线程私有,栈中的结构(局部变量,操作数栈,动态链接,方法出口)
      • 局部变量+操作数栈:上面的总流程说明关系(总结:局部变量放变量;操作数栈对值进行操作)
      • 动态链接:上面也有,详解----将符号引用转化为直接引用(符号引用在方法区中存放的类信息+静态变量+常量,当在程序中有所引用到就需要使用动态链接去直接引用在方法区中对应的内存地址)
    • 程序计数器:每个线程私有,记录程序运行到的行号,多线程情况下上下文切换依然会知道该线程跑到哪行程序
    • 堆:所有线程公用,存放对象(对象不一定在堆中,可能会在栈里,对象逃逸分析【下文介绍】)
      • 堆主要分为年轻代(eden区,survivor区【1区,2区】)+老年代
      • 详解见下
    • 方法区:所有线程公用,存放常量+类信息+静态资源
  • 3、堆的分析

    • 3.1 程序开始启动,对象会不断的生成存放到堆中
    • 3.2 先到eden区,当eden区满时触发字节码执行引擎的minorGC,根据可达性算法标记垃圾对象,通过GC算法去清理垃圾(GC下文会揭晓),去将没用的垃圾清理,将不是垃圾的对象放到survivor区中的1区,分代年龄加一
    • 3.3 对象继续生成,再满的活,同上操作,移到survivor区2区,分代年龄加一
    • 3.4 后面会不断触发minorGC,对象先在survivor区的1区和2区中来回存放
    • 3.5 当分代年龄到达15,或年轻代放不下了就会将对象放到老年代
    • 3.6 当老年代满的时候会触发字节执行引擎去fullGC,也是先根据可达性算法标记,根据gc算法去清理
  • 4、minorGC 和 fullGC

    • 4.1 只要GC都会触发STW(stop the world)停止所有用户线程
    • 4.2 minorGC速度比较快可以忽略不计,fullGC速度比较慢=》那么我们主要优化的就是避免fullGC
    • 4.3 STW为什么需要=》反证法:当没有STW的话,GC开始运作,通过可达性算法先标记垃圾;但是如果没STW那么用户线程继续运行,很有可能其中线程中方法执行完成,出栈销毁,那么其中的GCRoot是不是也就被销毁了,那么之前可达性算法标记的垃圾意义就没有了

三、调优参数设置

在这里插入图片描述

Spring Boot程序的JVM参数设置格式(Tomcat启动直接加在bin目录下catalina.sh文件里):

// 因为方法区默认使用的机器内存,如果不设置可能会将其暂满;如果太小可能会导致底层对方法区的频繁扩容,所以如下设置
Spring Boot程序的JVM参数设置格式(Tomcat启动直接加在bin目录下catalina.sh文件里)1 java ‐Xms2048MXmx2048MXmn1024MXss512K ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐jar microservice‐eurek a‐server.jar
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值