JVM详解

本文详细介绍了JVM的工作原理,包括JDK、JRE和JVM的关系,JVM的栈式架构以及为何选择栈而不是寄存器,JVM的生命周期从启动到结束的过程,类的加载、链接(验证、准备、解析)和初始化的详细步骤,以及类加载器的分类与双亲委派机制。此外,还探讨了运行时数据区,特别是PC寄存器、虚拟机栈、栈帧和局部变量表的功能和特点。最后,文章讨论了方法调用的静态解析与动态链接,虚方法与非虚方法的区别,以及invokedynamic指令的应用。
摘要由CSDN通过智能技术生成

JVM详解

1.JDK,JRE和JVM的关系

JDK: JAVA开发工具: 顾名思义 —> 用JAVA开发的时候要用到的工具 (javac.exe,java.exe)

JRE: JAVA运行环境: 顾名思义 —> 拿到工具了可不行,你必须有材料 —> JAVA开发所需要的基础包 (e.g:rt.jar) 里面有基础的类,String类等

JVM: JAVA虚拟机: 顾名思义 —> 工具有了,材料有了,就差一个工人 —> 一台虚拟的计算机这台计算机会将写的代码执行起来 —> 但是这个计算机么有图形操作界面,是不可以看见的,但是可以使用终端来对其操作 —>

​ 终端: 就是控制计算机的一个面板,如cmd

图形解释:

在这里插入图片描述

执行的过程
在这里插入图片描述

注意事项: JAVA是跨平台的语言,JVM是跨语言的平台

怎么理解上述两句话:

1.JAVA是跨平台的语言

在这里插入图片描述

什么是windows操作系统的计算机 —> CPU只认识Windows给的数据 —> 比如你输入了你好 —> windows就会给你变成二进制 0101000传给CPU --> CPU就会知道0101000是你好,给用户返回你好

什么是Linux操作系统的计算机 —> CPU只认识Linux给的数据 —> 比如你输入了你好 —> Linux就会给你变成二进制 1010111 传给CPU --> CPU就会知道1010111是你好,给用户返回你好

但是 JVM可以根据操作系统的不同将你好转变为该系统认识的指令

所以只要将 .java文件编译成.class文件给JVM处理就会在不同的平台显示相同的数据

2.JVM是跨语言的平台

JVM只识别.class文件,但是不同的语言都可以编译成class文件,比如go语言通过一定的编译就可以变成 .class文件,这样传给虚拟机都可以显示想要的数据

2.JVM的架构模型

看看物理机,寄存器,栈的相对位置

在这里插入图片描述

1.基于栈的架构

首先什么叫做基于栈的架构 —> 意思就是:围绕栈设计

1.让栈在内存中怎么开辟空间

2.栈在内存中开辟的大小怎么设计

等等 …

优点:

1.实现简单,适用于资源受限的设备 --> 实现:就是给内存条里面开内存,资源受限的设备–>比如机顶盒数据寄存器如果只有一个,但是对于机顶盒的寄存器也是它那一套的取法, 如果取数据还必须要三个数据寄存器就无法取出数据了

2.避免了寄存器的分配问题 —> 有很多寄存器但是每个寄存器的功能不一样,有的寄存器只执行指令后面有操作数,或者操作地址的指令,但是现在不需要分配了,JVM的指令就是0地址,也不用给那种只操作地址的寄存器了

3.指令集少,方便

比如就几百条指令,但是正常的指令可能会有上万条,也方便记忆

4.不需要硬件,方便迁移

就是在不同的操作系统上,只需要开辟内存里面的空间 —> 不需要硬件支持

在不同的操作系统上都可以下载 —> 方便迁移,意思就是方便来回在不同平台下载

2.基于寄存器的架构

首先什么叫做基于寄存的架构 —> 意思就是: 围绕寄存器来设计

1.让寄存器怎么工作更加快速找数据

2.需要几个寄存器才可以使用JVM

等等 …

优点:

1.查找速度快

把数据存放在内存不同的地方都可以快速找到

2.指令少,指令集多

比如 add 这条指令 就代表了 把第一个数取出来,然后再把第二个数取出来,然后两个数相加

指令集多 —> 就是 add …等几万条这种指令

3.JVM的架构采取基于栈的架构实现

原因:

1.跨平台性

2.基于寄存器的设计比较麻烦

3.基于寄存器设计跨平台性就比较差了

3.JVM的生命周期

1.JVM的启动

概念: 当用javac.exe将.java编译成.class,在使用java.exe来启动.class的时候,JVM就启动了,就相当于点击java.exe JVM就开启了

点击 java.exe --> 即使开启么有加载文件JVM也不会执行

2.JVM的执行

概念: 当开始加载你的.class文件的时侯,就开始运行了

加载 .class文件

3.JVM的结束

1.当class文件正常运行完成

2.class文件出错,比如出现异常,但是抓住异常不算出错

3.使用Java方法来直接结束 —> System.exit(); runtime.halt()

4.操作系统出错,导致java.exe直接停止

5.本地操作系统使用它的方法让程序停止

4.图形解释

在这里插入图片描述

4.类的加载过程

1.加载(Loading)

概念: 将本地或者网上的 .class 文件加载进来(加载过程是双亲委派机制)

1.通过类的完全限定类名(一个JVM中完全限定类名是唯一的),将这个.class文件以二进制流的形式加载进JVM内存中

2.加载进来会在内存中生成一个代表此类的class对象,这个对象就是以后创建对应此类实例的模板

2.链接(Linking)

概念: 验证–> 准备 --> 解析 统一称为链接

1.验证:

概念:检查class文件是否满足JVM规范

比如 —> 字节流文件的开头必须是什么 (CA FE BA BE)

在这里插入图片描述

2.准备:

概念:对class对象进行默认化设置

**对概念的理解:**因为现在是对class对象进行准备 --> 其实也就可以认为是对类对象进行准备 —> 类对象对应的就是类成员变量 进行准备

即给static修饰的成员进行默认化设置

可以通过对class文件进行反解释 —>

反编译: 就是将class文件变成java文件

反解释: 就是对class文件(流文件) 进行解释

工具: Jclasslib —> 直接在idea上安装插件即可

在这里插入图片描述

如图所示: 这个就是将class文件解释了,其中可以看出有一个 < clinit > 方法 , 这个就是类初始化的方法

比如:

private static int i = 10;

先对类变量进行默认话设置 —> 然后等初始化的时候在初始化

即: int 类型 所以先给 i = 0;

​ 其他类型的默认值: byte=0,short=0,int=0,char=’\u0000’,long=0,float=0.0,double=0.0,boolean=false

引用数据类型是null

注意事项:

1.只给类变量来设置默认值

2.如果用final来定义类变量必须给赋值(就不用默认化,直接就将值给类变量)

3.不给实例变量赋默认值

注意事项是在准备这个时期做的事情

3.解析:

概念:将符号引用变成直接引用

  • 符号引用:

概念: 以一组符号来描述所引用目标,符号引用的字面量形式明确定义在《Java虚拟机规范》的class文件中了

如图所示:符号引用
在这里插入图片描述

  • 直接引用:

概念: 就是一个明确地址

符号引用举例:

#1 ----> 转换成 Object的构造方法的地址

注意:这里将的是静态分析,动态链接在后面

两者共同点

都是用连将符号引用转换为直接引用的 —> 例如将 #1 ----> 转换成 Object的构造方法的地址

两者的区别

解析时期的不同:

1.静态解析: 在类加载阶段,或者第一次使用 —> 非虚方法的地址在这个时期加载 --> 非虚方法:static,private,final修饰的方法和,super.方法名,构造方法,后面会具体讲解非虚方法 —> 分析一次就好

2.动态链接: 在每一次运行期间 —> 虚方法在这个时期加载 --> 每一次执行都需要将 对应的符号引用变成直接引用

为什么说每一次运行期间?

代码说明

public class StaticAndDynamic {
   

    public static void main(String[] args) {
   
        StaticAndDynamic staticAndDynamic = new StaticAndDynamic();

        //线程一使用method()  ---> 第一次 ---> 会把method地址解析一次
        Thread dog = new Thread(new Runnable() {
   
            @Override
            public void run() {
   
                staticAndDynamic.method();
                System.out.println(Thread.currentThread().getName()+":使用一次method");
            }
        }, "线程一");

        //线程二使用method()  ---> 第二次 --> 会把method地址解析一次
        Thread cat = new Thread(new Runnable() {
   
            @Override
            public void run() {
   
                staticAndDynamic.method();
                System.out.println(Thread.currentThread().getName()+":使用一次method");
            }
        }, "线程二");


        dog.start();
        cat.start();
    }


    public void method(){
   
    }

}

直接引用举例

如下代码进行解释

public class Parsing {
   
    public static void main(String[] args) {
   
        int i = 10;
        int j = 20;
        int k = i + j;
    }
}

解释结果如下: —> javap -v Parsing.class

   public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         0: bipush        10
         2: istore_1
         3: bipush        20
         5: istore_2
         6: iload_1     //后面么 #为头的符号引用
         7: iload_2
         8: iadd
         9: istore_3
        10: return
      LineNumberTable:
        line 9: 0
        line 10: 3
        line 11: 6
        line 14: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  args   [Ljava/lang/String;
            3       8     1     i   I
            6       5     2     j   I
           10       1     3     k   I
}

如上图这几条指令:

6: iload_1
7: iload_2

指令的意思就是将局部变量表里面索引为1和2的局部变量地址压入操作数栈中

直接将索引为1的局部变量地址压入操作数栈 —> 不需要将"#… —> 解析成对应的地址"

3.初始化(initialization)

概念: 初始化类成员变量 —> 初始化本类的时候先会初始化父类
在这里插入图片描述

上图是么写构造方法 --> 但是解释器有 < init > 这个方法

在这里插入图片描述

使用 < clinit >方法进行初始化,如上图所示在java反解释的图形化界面就可以看出—>

他是从.class文件里解释出来的, 那么意思就是java代码中应该有这个方法 —>

但是实际代码是么有这个方法的 —> 而且按照方法的加载顺序来看它应该是最后一个加载进来的 -->

那么就可以认为是一个隐藏的方法 —>

但是它在初始化的时候生效,作用就是给类成员变量初始化

private static int num = 10;
static{
   
    num = 20;
}

javac会将 类成员静态代码块里 的代码全部拿出来, 然后在这个阶段使用 < clinit > 这个方法执行, 并且是按照代码顺序来执行的

注意1:非法前向引用

public class Init extends InitFather{
   
    //证明类构造器方法只初始化 类成员变量
    private static int num = 10;
    private int num2 = 10;
    static{
   
        num = 20;
        System.out.println(num);
        System.
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值