虚拟机类加载机制

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 20:47   1039   0

1.虚拟机类加载机制:Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验, 转换解析和初始化,最终形成可以被虚拟机直接使用的java类型 的过程。

Java 语言中, 类的加载,连接和初始化都是在程序运行期间完成的,但为java提共了极高的扩展性和灵活性。java天生的可以动态扩展的语言特性就是依赖运行期动态加载和动态链接的这个特点。

2.类加载的时机

类的生命周期:加载 ,验证, 准备, 解析, 初始化, 使用, 卸载

六种情况必须对类立即进行初始化:

主动引用:

1)遇到new, getstatic, putstatic,invokestatic这第四条指令的时候:

使用new关键字初始化对象

读取或者设置一个静态字段

调用一个静态的方法

2)使用reflect进行反射调用的时候

3)初始化类的时候如果发现父类没有进行过初始化, 则需要先触发父类的初始化

4)虚拟机启动的时候需要指定一个主类,需要先初始化这个主类

5)JDK7 中MethodHandle实例的最后解析结果为**** 方法句柄为

6)JDK8中加入default修饰的方法接口

除了以上之外的所有引用都是被动引用

被动引用:

子类调用父类的静态变量,只会初始化父类不会初始化子类。

对于调用常量, 并不会触发初始化, 因为常量在编译阶段就存入常量池中了,本质上并没又引用到常量的类

对于静态字段,只有直接定义这个字段的类才会被初始化。

3.类加载的过程

加载: 开发人员可控性最强的阶段。

java虚拟机需要完成以下三种事:

1)通过一个类的全限定名来获取定义此类的二进制字节流。

2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构

3)在内存中生成一个代表这个类的class对象,作为方法区这个类的各种数据的访问入口。

非数组类型:内置类加载器,和自定义类加载器(重写一个类加载器的findClass() 或 loadClass()方法)进行加载

数据类型数据:由java虚拟机直接在内存中动态构造出来。如果类型是引用类型, 则采用递归,这个数组将被表示在加载该组件类型的类加载器的类名称空间上。如果不是引用类型,则与引导类加载关连。可访问性和数据库类一致,如果不是引用类型,访问性是public。

加载阶段和连接阶段部分是交叉进行的

验证:是连接阶段第一步,目的是确保Class文件的字节流中包含的信息符合约束

四个阶段的验证:文件格式验证, 元数据验证,字节码验证,符号引用验证。

1)文件格式验证:验证字节流是否符合Class文件格式的规范。主要目的是保证输入的字节流能正确的解析并保存于方法区之内,格式上符合描述一个Java类型信息的要求。

2)元数据验证:对字节码描述的信息进行语义分析,元数据信息进行语义校验。

是否有父类,是否继承不该继承的,抽象类是否实现父类的接口,类中字段。方法是否与父类产生矛盾。

3)字节码验证 :通过数据流分析和控制流分析,确定语义是合法的,符合逻辑的。对类的实体进行校验分析。StackMapTable描述了方法体的所有的基本块,所有只需要检查StackMapTable中的记录是否合法即可。

4) 符号引用验证:将符号引用转换成直接引用,对类自身意外的各类信息进行匹配性校验,确保解析行为能正常执行。

准备: 正式为类中的变量(static修饰的变量)分配内存并设置类变量的初始值

public static int v = 123, 准备阶段v的值仍是0,知道初始化阶段才是123.

特殊情况: public static final int v =123 , 这个情况会有一个ConstantValue属性,在准备阶段就会根据ConstantValue设置v为123

解析:将常量池中内的符号引用替换为直接引用的过程。

1)类和接口的解析

2)字段解析 :解析一个未被解析过的字段符号引用, 先对字段表内class_index项中索引的CONSTANT_Class_Info符号进行解析,也就是字段所属的类或接口的符号引用。

3)方法解析

初始化:执行类构造器<clinit>()方法,<clinit>()是由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的

类加载器:通过一个类的限定名来捕获描述该类的二进制字节流, 这个动作的代码就是类加载器。

双亲委派模型

启动类加载器:负责加载javahome/lib目录,将类库加载到虚拟机内存中。

扩展类加载器:负责加载javahome/lib/ext目录,

应用程序类加载器:负责加载用户类路径上(classpath)的所有的类库。

加载器的调用过程: 先检查请求加载的类型是否已经被加载过,如没有则调用父类加载器的loadClass()方法, 如父类加载器为空则默认使用启动类加载器作为父类加载器,假如父类加载器加载失败,抛出ClassNotFoundException异常的话,才调用自己的findClass()方法尝试进行加载。

基础类型调回用户代码: 线程上下文类加载器,如果创建线程的时候还没有设置,则从父线程继承一个,如果全局范围内都没有设置, 则调用应用程序的类加载器。

java模块化系统:

类路径ClassPath

模块路径ModulePath

JDK9 中: 当平台及应用程序类加载器收到类加载需求,在委派给父类加载器加载前,要先判断该类是否能够归属到某一个系统模块中,如果可以找到这样的归属关系,就要优先委派给负责那个模块的加载器完成。

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP