JVM基础中篇-字节码与类的加载

学习自:哔哩哔哩尚硅谷

JVM基础中篇-字节码与类的加载

1、Class文件结构

1.1、概述

Java语言:跨平台的语言
字节码文件的跨平台性
Java虚拟机:跨语言的平台

想要让一个Java程序正确地运行在JVM中,Java源码就必须要被编译为符合JVM规范的字节码

  • 前端编译器的主要任务就是负责将符合Java语言规范的Java代码转换为符合JVM规范的字节码文件
  • javac是一种能将Java源码编译为字节码的前端编译器
  • javac编译器在将java源码编译为一个有效字节码文件过程中经历了4个步骤,分别是:词法解析语法解析语义解析以及生成字节码

1.2、Class文件

字节码文件里是什么?

  • 源代码经过编译器编译之后会生成一个字节码文件,字节码是一种二进制的类文件,它的内容是JVM指令

什么是字节码指令(byte code)?

  • 操作码(+ 操作数)
  • Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义的操作码(opcode)以及跟随其后的零至多个代表此操作所需参数的操作数(operand)所构成。虚拟机中许多指令并不包含操作数,只有一个操作码
1.3、Class文件结构

Class类的本质:

  • Class文件是一组以8字节为基础单位的二进制流
  • Class文件并不一定是以磁盘文件的形式存在

Class文件格式:

  • 没有任何的分隔符号。其中的数据项,无论是字节熟顺序还是数量,都是被严格限定的,长度多少、先后顺序都是不允许被改变的
  • Class文件格式采用一种类似于C语言结构体的方式进行数据存储,这种结构中只有两种数据类型:无符号数和表
    无符号数属于基本数据类型
    表是由多个无符号数或者其他表作为数据项构成的复合数据类型

Class文件的总体结构:

  • magic(魔数)
  • Class文件版本
  • 常量池
  • 访问标志
  • 类索引、父类索引,接口索引集合
  • 字段表集合
  • 方发表集合
  • 属性表集合
  • JVM基础中篇-字节码与类的加载_第1张图片

1、Magic Number(魔数)

  • 每个Class文件开头的4个字节的无符号整数称为魔数
  • 它的唯一作用是确定这个文件是否为一个能被虚拟机接受的有效合法的Class文件。即:魔数是Class文件的标识符
  • 魔数值固定为0xCAFEBABE,不会改变
  • 如果一个Class文件不以0xCAFEBABE开头,虚拟机在进行文件校验的时候会直接抛出以下错误:
    在这里插入图片描述
  • 使用魔数而不是扩展名来进行识别主要是基于安全方面的考虑,因为扩展名可以随意地改动

2、Class文件的版本号

  • 紧接着魔数的4个字节存储的是Class文件的版本号。同样也是4个字节。第5个和第6个字节所代表的含义就是编译的副版本号mino_version,而第7个和第8个字节就是编译主版本号major_version
  • 它们共同构成了class文件的格式版本号。譬如某个Class文件的主版本号为M,副版本号为m,那么这个Class文件格式版本号就确定为M.m
  • 版本号与Java编译器的对应关系如下:
    JVM基础中篇-字节码与类的加载_第2张图片
  • Java的版本号是从45开始的,JDK1.1之后的每个JDK大版本发布朱版本号向上加1
  • 不同版本的Java编译器编译的Class文件对应的版本是不一样的。目前,高版本的Java虚拟机可以执行由低版本编译器生成的Class文件,但是低版本的Java虚拟机不能执行由高版本编译器生成的Class文件,否则会JVM抛出UnsupportedClassVersionError异常
  • 在实际应用中,由于开发环境和生产环境不同,可能会导致该问题的发生。因此,需要特别注意版本的一致性

3、常量池:存放所有常量

  • 常量池是Class文件内容最为丰富的区域之一。常量池对于Class文件中的字段和方法解析也有着至关重要的作用
  • 随着Java虚拟机的不断发展,常量池的内容也日渐丰富。可以说,常量池是整个Class文件的基石
  • 在版本号后,紧跟着的是常量池的数量,以及若干个常量池表项
  • 常量池中常量的数量是不固定的,所有在常量池的入口需要放置一项u2类型的无符号数,代表常量池容量计数值(constant_pool_count)。与Java中语言习惯不一样的是,这个容量计数是从1开始而不是0开始的
    JVM基础中篇-字节码与类的加载_第3张图片
    由上表可见,Class文件使用了一个前置的容量计数器(constant_pool_count)加若干个连续的数据项(constant_pool)的形式来描述常量池内容。我们把这一系列连续常量池数据称为常量池集合
  • 常量池表项中,用于存放编译时期生成的各种字面量符号引用,这部分内容将在类加载进入方法区的运行时常量池中存放

3.1、常量池计数器(constant_pool_count)

JVM基础中篇-字节码与类的加载_第4张图片
3.2、常量池表(constant_pool)
在这里插入图片描述
JVM基础中篇-字节码与类的加载_第5张图片
JVM基础中篇-字节码与类的加载_第6张图片
JVM基础中篇-字节码与类的加载_第7张图片
在这里插入图片描述
常量类型和结构:

总结:
JVM基础中篇-字节码与类的加载_第8张图片
JVM基础中篇-字节码与类的加载_第9张图片
4、访问标识

  • 在常量池后,紧跟着访问标记,该标记使用两个字节标识,用于识别一些类或者接口层次的访问信息,包括:这个是类还是接口;是否定义为抽象类型;如果是类的话,是否被声明为final等。各种访问标记如下图所示:
    JVM基础中篇-字节码与类的加载_第10张图片
  • 类的访问权限通常为 ACC_ 开头的常量
  • 每一种类型的表示都是通过访问标记的32位中的特定为来实现的。比如,若是public final 类,则该类标记为ACC_PUBLIC | ACC_FINAL
  • 使用ACC_SUPER可以让类更准确地定位到父类的方法super.method(),现代编译器都会设置并且使用这个标记
  • JVM基础中篇-字节码与类的加载_第11张图片
  • JVM基础中篇-字节码与类的加载_第12张图片

5、类索引,父类索引,接口索引集合

  • 在访问接口后,会指定该类的类别、父类类别以及实现的接口,格式如下:
    在这里插入图片描述
  • 这三项数据来确定这个类的继承关系
    1、类索引用于确定这个类的全限定名
    2、父类索引用于确定这个类的父类全限定名。由于Java语言不允许多重继承,所以父类索引只有一个,除了java.lang.Object之外,所有的Java类都有父类,因此除了java.lang.Object外,所有java类的父类索引都不为0
    3、接口索引集合就用来描述这个类实现了哪些接口,这些被实现的接口将按implements语句(如果这个类本身就是一个接口,则应当是extends语句)后的接口顺序从左到右排列在这个接口索引集合中

在这里插入图片描述
在这里插入图片描述

6、字段表集合
JVM基础中篇-字节码与类的加载_第13张图片

  • 字段计数器:
    在这里插入图片描述

  • 字段表:
    JVM基础中篇-字节码与类的加载_第14张图片
    JVM基础中篇-字节码与类的加载_第15张图片
    JVM基础中篇-字节码与类的加载_第16张图片
    JVM基础中篇-字节码与类的加载_第17张图片

7、方法表集合

  • 方法计数器
  • 方法表

JVM基础中篇-字节码与类的加载_第18张图片

方法计数器:
JVM基础中篇-字节码与类的加载_第19张图片
方法表:
JVM基础中篇-字节码与类的加载_第20张图片
JVM基础中篇-字节码与类的加载_第21张图片

8、属性表集合

  • 属性计数器
  • 属性表

在这里插入图片描述

属性表
JVM基础中篇-字节码与类的加载_第22张图片
code属性:
JVM基础中篇-字节码与类的加载_第23张图片

1.4、使用javap指令解析Class文件

1、解析字节码的作用:
JVM基础中篇-字节码与类的加载_第24张图片
2、javac -g操作:
在这里插入图片描述
3、javap的用法
JVM基础中篇-字节码与类的加载_第25张图片
JVM基础中篇-字节码与类的加载_第26张图片

4、javap指令小结:
JVM基础中篇-字节码与类的加载_第27张图片

2、字节码指令集与解析

2.1、概述

JVM基础中篇-字节码与类的加载_第28张图片
执行模型:
JVM基础中篇-字节码与类的加载_第29张图片
字节码与数据类型:
JVM基础中篇-字节码与类的加载_第30张图片
在这里插入图片描述
指令分类:
JVM基础中篇-字节码与类的加载_第31张图片

2.2、加载与存储指令

JVM基础中篇-字节码与类的加载_第32张图片
回顾虚拟机栈:
JVM基础中篇-字节码与类的加载_第33张图片
JVM基础中篇-字节码与类的加载_第34张图片
回顾局部变量表:
JVM基础中篇-字节码与类的加载_第35张图片

  • 局部变量表中的变量也是重要的垃圾回收根节点,只要被局部变量表中直接或间接引用的对象都不会被回收
  • 在方法执行时,虚拟机使用局部变量表完成方法的传递

1、局部变量压栈指令:
JVM基础中篇-字节码与类的加载_第36张图片
2、常量入栈指令:
JVM基础中篇-字节码与类的加载_第37张图片
在这里插入图片描述

JVM基础中篇-字节码与类的加载_第38张图片
3、出栈装入局部变量表指令:
JVM基础中篇-字节码与类的加载_第39张图片

2.3、算术指令

在这里插入图片描述
JVM基础中篇-字节码与类的加载_第40张图片
计算运算指令:
在这里插入图片描述
比较运算指令:
JVM基础中篇-字节码与类的加载_第41张图片

2.4、类型转换指令

在这里插入图片描述
宽化类型转换:
JVM基础中篇-字节码与类的加载_第42张图片
JVM基础中篇-字节码与类的加载_第43张图片
窄化类型转换:
JVM基础中篇-字节码与类的加载_第44张图片
JVM基础中篇-字节码与类的加载_第45张图片

2.5、对象的创建与访问指令

在这里插入图片描述
1、创建指令:
JVM基础中篇-字节码与类的加载_第46张图片
2、字段访问指令:
JVM基础中篇-字节码与类的加载_第47张图片
3、数组操作指令:
JVM基础中篇-字节码与类的加载_第48张图片
JVM基础中篇-字节码与类的加载_第49张图片
4、类型检查指令:
在这里插入图片描述

2.6、方法调用与返回指令

1、方法调用指令
JVM基础中篇-字节码与类的加载_第50张图片
2、方法返回指令:
在这里插入图片描述
JVM基础中篇-字节码与类的加载_第51张图片

2.7、操作数栈管理指令

在这里插入图片描述
JVM基础中篇-字节码与类的加载_第52张图片

2.8、控制转移指令

在这里插入图片描述
比较指令
JVM基础中篇-字节码与类的加载_第53张图片

1、条件跳转指令:
JVM基础中篇-字节码与类的加载_第54张图片
JVM基础中篇-字节码与类的加载_第55张图片
2、比较条件跳转指令
JVM基础中篇-字节码与类的加载_第56张图片
3、多条件分支跳转
JVM基础中篇-字节码与类的加载_第57张图片
在这里插入图片描述
4、无条件跳转指令
JVM基础中篇-字节码与类的加载_第58张图片

2.9、异常处理指令

1、抛出异常指令:
JVM基础中篇-字节码与类的加载_第59张图片
2、异常处理和异常表:
在这里插入图片描述

3、同步控制指令

java虚拟机支持两种同步结构:方法级的同步方法内部一段指令序列的同步,这两种同步都是使用monitor来支持的

1、方法级的同步:
在这里插入图片描述
2、方法内指定指令序列的同步:
JVM基础中篇-字节码与类的加载_第60张图片
JVM基础中篇-字节码与类的加载_第61张图片

3、类的加载过程详解

3.1、概述

从java中数据类型分为基本数据类型和引用数据类型。基本数据类型由虚拟机预先定义,引用数据类型则需要进行类的加载

按照java虚拟机规范,从class文件到加载内存中的类,到类卸载出内存为止,它的整个生命周期包括如下7个阶段:
JVM基础中篇-字节码与类的加载_第62张图片

  • 其中,验证、准备、解析3个部分称为链接(Linking)

从程序中类的使用过程看:
在这里插入图片描述
一些相关的面试题:

  • 描述一下JVM加载Class文件的原理机制
  • 类加载过程
  • 类加载的时机
  • java类加载过程
  • JVM中类加载机制,类加载过程?

3.2、过程1:Loading(加载)阶段

1、加载完成的操作:
JVM基础中篇-字节码与类的加载_第63张图片
2、二进制流的获取方式:
JVM基础中篇-字节码与类的加载_第64张图片
3、类模型与Class实例的位置:
在这里插入图片描述
JVM基础中篇-字节码与类的加载_第65张图片
外部可以通过访问代表Order类的Class对象来获取Order的类数据结构
JVM基础中篇-字节码与类的加载_第66张图片
4、数组类的加载:
JVM基础中篇-字节码与类的加载_第67张图片

3.3、过程2:Linking(链接)阶段

1、验证阶段:
当类加载到系统后,就开始链接操作,验证是链接操作的第一步

它的目的是保证加载的字节码是合法、合理并符合规范的

JVM基础中篇-字节码与类的加载_第68张图片
JVM基础中篇-字节码与类的加载_第69张图片
JVM基础中篇-字节码与类的加载_第70张图片
JVM基础中篇-字节码与类的加载_第71张图片
2、准备阶段:
JVM基础中篇-字节码与类的加载_第72张图片

  • 默认赋值

在这里插入图片描述
注意:如果使用字面量的方式定义一个字符串常量的话,也是在解析环节直接进行显式赋值。

3、解析阶段:
在准备阶段完成后,就进入了解析阶段
解析阶段,简言之,将类、接口、字段和方法的符号引用转为直接引用
在这里插入图片描述
在这里插入图片描述
JVM基础中篇-字节码与类的加载_第73张图片
JVM基础中篇-字节码与类的加载_第74张图片

3.4、过程3:Initialization(初始化)阶段

1、static 与 final 的搭配问题:
初始化阶段,简言之,为类的静态变量赋予正确的初始值

JVM基础中篇-字节码与类的加载_第75张图片
JVM基础中篇-字节码与类的加载_第76张图片
在链接阶段的准备环节赋值的情况:

  • 对于基本数据类型的字段来说,如果使用 static final修饰,则显式赋值(直接赋值常量,而非调用方法)通常是在链接阶段的准备环节进行
  • 对于String来说,如果使用字面量的方式赋值,使用 static final 修饰的话,则显式赋值通常是在链接阶段的准备环节进行

在初始化阶段()中赋值情况:

  • 上述两种情况之外

最终结论:

  • 使用 static final修饰,且显示赋值中不涉及到方法或构造器调用的基本数据类型或String类型的显示赋值,是在链接阶段的准备环节进行

2、()的线程安全性:
JVM基础中篇-字节码与类的加载_第77张图片
3、类的初始化情况:主动使用vs被动使用:
JVM基础中篇-字节码与类的加载_第78张图片
JVM基础中篇-字节码与类的加载_第79张图片
JVM基础中篇-字节码与类的加载_第80张图片

3.5、过程4:类的Using(使用)

在这里插入图片描述

3.6、过程5:类的Unloading(卸载)

JVM基础中篇-字节码与类的加载_第81张图片
JVM基础中篇-字节码与类的加载_第82张图片
JVM基础中篇-字节码与类的加载_第83张图片

4、类的加载器

4.1、概述

类加载器是JVM执行类加载机制的前提

在这里插入图片描述
JVM基础中篇-字节码与类的加载_第84张图片
1、面试题:
什么是双亲委派机制?优点是什么?讲一下双亲委派机制

2、类加载的分类:
在这里插入图片描述
3、类加载器的必要性:
JVM基础中篇-字节码与类的加载_第85张图片
4、类加载器的命名空间:
JVM基础中篇-字节码与类的加载_第86张图片
5、类加载机制的基本特征:
JVM基础中篇-字节码与类的加载_第87张图片

4.2、类的加载器分类

JVM支持两种类型的类加载器:

  • 引导类加载器(启动类加载器)
  • 自定义类加载器:一般是指程序中开发人员自定义的一类类加载器,但是Java虚拟机中是将所有派生于抽象类ClassLoader的类的加载器都划分为自定义类自定义类加载器
    JVM基础中篇-字节码与类的加载_第88张图片
  • 除了顶层的启动类加载器外,其余的类加载器都应当有自己的“父类”加载器
  • 不同类加载器看似是继承关系,实际上是包含关系。在下层加载器中,包含着上层加载器的引用

引导类加载器(启动类加载器):
JVM基础中篇-字节码与类的加载_第89张图片
扩展类加载器:
在这里插入图片描述
应用程序类加载器(系统类加载器):
JVM基础中篇-字节码与类的加载_第90张图片
用户自定义类加载器:
JVM基础中篇-字节码与类的加载_第91张图片

4.3、测试不同类的加载器

每个Class对象都包含一个定义它的ClassLoader的一个引用。

获取ClassLoader的途径:
JVM基础中篇-字节码与类的加载_第92张图片

  • 说明
    JVM基础中篇-字节码与类的加载_第93张图片

4.4、ClassLoader源码解析

JVM基础中篇-字节码与类的加载_第94张图片
JVM基础中篇-字节码与类的加载_第95张图片
JVM基础中篇-字节码与类的加载_第96张图片
JVM基础中篇-字节码与类的加载_第97张图片
在这里插入图片描述
Class.forName()与ClassLoader.loadClass()
JVM基础中篇-字节码与类的加载_第98张图片

4.5、双亲委派模型

1、定义与本质:
JVM基础中篇-字节码与类的加载_第99张图片
JVM基础中篇-字节码与类的加载_第100张图片
JVM基础中篇-字节码与类的加载_第101张图片
2、双亲委派机制的优势与劣势:
JVM基础中篇-字节码与类的加载_第102张图片
JVM基础中篇-字节码与类的加载_第103张图片
JVM基础中篇-字节码与类的加载_第104张图片
破坏双亲委派机制1:
在这里插入图片描述
破坏双亲委派机制2:线程上下文加载器
JVM基础中篇-字节码与类的加载_第105张图片
JVM基础中篇-字节码与类的加载_第106张图片
JVM基础中篇-字节码与类的加载_第107张图片
在这里插入图片描述
破坏双亲委派机制3:
JVM基础中篇-字节码与类的加载_第108张图片
JVM基础中篇-字节码与类的加载_第109张图片
热替换的实现:
JVM基础中篇-字节码与类的加载_第110张图片
JVM基础中篇-字节码与类的加载_第111张图片

4.6、沙箱安全机制

沙箱安全机制

  • 保证程序安全
  • 保护Java原生的JDK代码

JVM基础中篇-字节码与类的加载_第112张图片

4.7、自定义类的加载器

JVM基础中篇-字节码与类的加载_第113张图片
在这里插入图片描述
实现方式:
JVM基础中篇-字节码与类的加载_第114张图片
在这里插入图片描述

4.8、Java9新特性

JVM基础中篇-字节码与类的加载_第115张图片
在这里插入图片描述
JVM基础中篇-字节码与类的加载_第116张图片
在这里插入图片描述
JVM基础中篇-字节码与类的加载_第117张图片
JVM基础中篇-字节码与类的加载_第118张图片
左图:JDK 9之前;右图:JDK9

你可能感兴趣的:(jvm,java)