(二)浅谈JVM的类加载知识

一、什么是类加载机制

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

二、类加载的时机

在介绍类加载的时机之前,先讲一下类从被加载到虚拟机内存中开始到卸载出内存为止的整个生命周期:
在这里插入图片描述

加载、验证、准备、初始化和卸载这五个阶段顺序是确定的,类加载过程必须按照这种顺序开始,而解析过程则不一定

类加载过程的第一步加载时机没有进行强制约束,交由虚拟机具体实现自由把握。

三、类加载过程

Java虚拟机类加载的全过程:加载、验证、准备、解析、初始化

(1)加载:

加载阶段,虚拟机需要完成以下三件事:

  • 1)通过一个类的全限定名来获取定义此类的二进制字节流
  • 2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  • 3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

数组类本身不通过类加载器创建,它由Java虚拟机直接创建

加载阶段完成之后,虚拟机外部的二进制字节流按照虚拟机所需的格式存储在方法区之中,方法区中的数据存储格式由虚拟机实现自行定义,虚拟机规范未规定此区域的具体数据结构,然后在内存中实例化一个java.lang.Class类的对象(JDK版本是1.7)

(2)验证:

验证的目的是为了确保class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证阶段大致会完成下面四个阶段的检验动作:文件格式验证、元数据验证、字节码验证、符号引用验证。
文件格式验证: 验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理;
元数组验证: 对类的元数据信息进行语义校验,保证不存在不符合Java语言规范的元数据信息;
字节码验证: 通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的;
符号引用验证: 对类本身以外的信息进行匹配性校验,确保解析动作能正常执行

(3)准备:

正式为类变量分配内存并设置类变量初始值的阶段。

(4)解析:

虚拟机将变量池内的符号引用替换为直接引用的过程

(5)初始化:

类初始化阶段是类加载过程的最后一步,前面的类加载过程中,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全有虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码。

四、类加载器:

启动类加载器(Bootstrap ClassLoader): 使用c++实现,无法被Java程序直接引用;负责将存放在<JAVA_HOME>\lib目录中,或者被-Xbootclasspath参数所指定的路径中并且是虚拟机识别的类库加载到虚拟机内存中;
扩展类加载器(Extension ClassLoader): 负责加载<JAVA_HOME>\lib\ext 目录中,或者被java.ext.dirs 系统变量所指定路径中的所有类库,可以直接使用扩展类加载器;
应用程序(系统类)加载器(Application ClassLoader): 负责加载用户类路径(classpath)上指定的类库;
img

Tip:ExtClassLoader 和AppClassLoader直接并没有继承关系只是AppClassLoader中的parent属性是ExtClassLoader

五、双亲委派机制:

(1)寻找方式:APP —> EXT —> BOOT
(2)大致流程:

  • 首先,检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。
  • 如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加载器加载(即调用parent.loadClass(name, false);)或者是调用bootstrap类加载器来加载。
  • 如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的 findClass方法来完成类加载

如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,因此所有的加载请求最终都会传到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载

(3)双亲委派源码实现机制:ClassLoader.loadClass()

(4)优势:
1、避免类的重复加载
2、保护程序安全,防止核心API被随意篡改


本文来源:《深入理解Java虚拟机(第2版)》 周志明著

猜你喜欢

转载自blog.csdn.net/qq_42908549/article/details/107417590