美文网首页
JVM知识体系-01类加载机制

JVM知识体系-01类加载机制

作者: 云芈山人 | 来源:发表于2023-02-09 20:01 被阅读0次

一、思维导图

01类加载机制.png

二、大纲

2.1 类的加载是什么?

  • 将类(.class)文件中的二进制数据读入内存,并放在运行时数据库的方法区内,在堆区创建java.lang.Class对象(封装类在方法区的数据结构)。

  • 类加载最终产品是堆区的Class对象(封装了类在方法区的数据结构,并向Java程序员提供了访问方法区内的数据结构的接口)。

2.2 类的生命周期(重要)

2.2.1 类的生命周期图

类的生命周期.png

注意:顺序执行的意思是开始,而不是完成后进行下一步

2.2.2 过程

1. 加载

  • 查找并加载类的二进制数据,在Java堆中创建java.lang.Class对象

    JVM需要做的三件事

    1. 通过类的全限定名获取其定义的二进制字节流
    2. 将此字节流所代表的静态存储结构转化为方法区的运行时数据结构
    3. 在Java堆中创建java.lang.Class对象,作为访问方法区数据的入口
  • 获取class文件的5种方式

    1. 本地系统中直接加载
    2. 网络下载.class文件
    3. 从zip,jar等归档文件中加载.class文件
    4. 专有数据库提取.class文件
    5. Java源文件动态编译为.class文件

注意:此阶段可控性最强,可用系统自带类加载器也可自定义类加载器

2. 连接

2.1 验证

四个阶段

  • 文件格式
    如0xCAFEBABE开头等
  • 元数据
    语义分析,如是否有父类(不含Object)
  • 字节码
    语义合法
  • 符号引用验证
    保证解析能正确执行

注意:重要但不必须,如所引用类经反复验证,可采用-Xverifynone关闭

2.2 准备

为类的静态变量分配内存,并将其初始化默认值

注意:

  • 仅包括类变量(static),不含实例变量(在实例化时随对象一块分配在堆中)
  • 此初始值指数据类型默认的零值,非显示赋予的值
  • 若类字段的字段属性表中存在ConstantValue属性,即同时被final和static修饰,在准备阶段变量会被初始化为ConstantValue属性做指定的值
2.3 解析

把类中的符号引用(一组符号来描述)转化为直接引用(直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄)


直接指针访问对象.png
句柄访问对象.png
  • 符号引用

    • 接口
    • 字段
    • 类方法
    • 接口方法
    • 方法类型
    • 方法句柄
    • 调用点限制符
  • 加载顺序不确定
    某些情况下在初始化之后,为了支持Java语言的运行时绑定(动态绑定或晚期绑定)

3. 初始化

为类的静态变量赋予正确的初始值

设定的两种方式

  • 声明类变量是指定初始值
  • 使用静态代码块为类变量指定初始值

JVM初始化步骤

  • 若此类未被加载和连接,则程序先加载并连接该类
  • 若此类的直接父类未初始化,则先初始化其直接父类
  • 若类中有初始化语句,则系统依次执行这些初始化语句

类初始化时机

只有当对类的主动使用的时候才会导致类的初始化

  • 六种主动使用
    1. 创建类的实例,即new
    2. 访问某个类或接口的静态变量,或对该静态变量赋值
    3. 调用类的静态方法
    4. 反射(如Class.forName("com.insigim.Test"))
    5. 初始化某个类的子类,则父类也被初始化
    6. Java虚拟机启动时被标明为启动类的类(Java Test),直接使用java.exe命令来运行某个主类

4. 使用

  • new出对象程序中使用

5. 卸载

  • 执行垃圾回收

2.2.3 几种情况下会结束生命周期

  1. 执行System.exit()方法
  2. 程序正常执行结束
  3. 程序执行过程中遇到了异常或错误而异常终止
  4. 由于操作系统出现错误而导致Java虚拟机进程终止

2.3 类加载器

1. 几种加载器的层次关系

类加载器层次关系.png

2. 两种角度分类

2.1 JVM角度

启动类加载器
  • 使用C++实现(仅限Hotspot)
  • 虚拟机自身一部分
所有其他类加载器
  • Java语言实现
  • 独立虚拟机之外
  • 继承抽象类java.lang.ClassLoader
  • 需由启动类加载器加载到内存中才能加载其他的类

2.2 开发人员角度

启动类加载器
  • Bootstrap ClassLoader,C++实现
  • 负责加载存放在JDK\jre\lib下,或被-Xbootclasspath参数指定的路径中,且能被JVM识别的类库(如rt.jar,所有java.开头的类)
  • 无法被Java程序直接引用
扩展类加载器
  • Extension ClassLoader,由sun.misc.launcher$ExtClassLoader实现
  • 负责加载JDK\jre\lib\ext目录中,或java.ext.dirs系统变量指定的路径中的所有类库(如javax.开头的类)
  • 开发者可直接使用扩展类加载器
应用程序类加载器
  • Application ClassLoader,由sun.misc.Launcher$AppClassLoader实现
  • 负载加载用户类路径(ClassPath)所指定的类
  • 开发者可直接使用该类加载器,若应用程序未自定义自己的类加载器,此即为默认的类加载器

3. JVM类加载机制(重要)

  • 全盘负责
    当一个类加载器加载类时,此类所依赖和引用的其它Class都由此加载器负责,除非显示使用另一个类加载器

  • 父类委托
    先让父类加载器试图加载,只有父类无法加载,才尝试从自己的类路径加载该类

  • 缓存机制
    保证所有加载过的Class都被缓存,程序需使用时优先从缓存中加载,没有先找二进制数据在堆中创建Class对象,加入缓存,所以每次Class修改需重启JVM,修改才生效

2.4 类的加载

有三种方式

  1. 命令行启动应用时候由JVM初始化加载
  2. 通过Class.forName()方法动态加载
  3. 通过ClassLoader.loadClass()方法动态加载

Class.forName()与ClassLoader.loadClass()加载的区别?

  • Class.forName():将类的.class文件加载到JVM中之外,还会对类进行解释,执行类中的static块
  • ClassLoader.loadClass():只干一件事,就是将.class文件加载到JVM中,不执行statiic中内容(newInstance才会执行)
  • Class.forName(name,initialize,loader)带参函数也可控制是否加载static块,并且只有调用newInstance()方法采用调用构造函数,创建类的对象

2.5 双亲委派模型(重要)

工作流程

若类加载器收到类加载的请求,首先把请求委托父加载器去完成,依次向上,因此所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围内没有找到所需的类时(无法加载),子加载器才尝试自己去加载该类

双亲委派机制

  1. 当AppClassLoader加载一个class时,先委派ExtClassLoader加载
  2. 当ExtClassLoader加载一个class时,先委派BootStrapClassLoader去加载
  3. 若BootStrapClassLoader加载失败(JDK\jre\lib无法找到该类),使用ExtClassLoader加载
  4. ExtClassLoader也加载失败(JDK\jre\lib\ext无法找到该类),则由AppClassLoader加载,若还失败则报出异常ClassNotFoundException

源码分析

public Class<?> loadClass(String name)throws ClassNotFoundException {
       return loadClass(name, false);
}

protected synchronized Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {
       // 首先判断该类型是否已经被加载
       Class c = findLoadedClass(name);
       if (c == null) {
           //如果没有被加载,就委托给父类加载或者委派给启动类加载器加载
           try {
               if (parent != null) {
                    //如果存在父类加载器,就委派给父类加载器加载
                   c = parent.loadClass(name, false);
               } else {
               //如果不存在父类加载器,就检查是否是由启动类加载器加载的类,
              //通过调用本地方法native Class findBootstrapClass(String name)
                   c = findBootstrapClass0(name);
               }
           } catch (ClassNotFoundException e) {
            // 如果父类加载器和启动类加载器都不能完成加载任务,才调用自身的加载功能
               c = findClass(name);
           }
       }
       if (resolve) {
           resolveClass(c);
       }
       return c;
   }

意义

  • 系统类防止内存中出现多份同样的字节码
  • 保证Java程序安全稳定运行

JDK1.9后的委派模型

JDK9后的类加载器委派关系.png

2.6 自定义类加载器

一般情况,由三种类加载器互相配合进行加载,如有必要,也可加入自定义类加载器
  • 如应用是通过网络来传输Java类的字节码,为保证安全性,这些字节码经过加密处理,系统类加载器无法加载,则需要自定义加载器实现
  • 一般继承loadClasser类,只需重写findClass方法即可
  • 核心在于对字节码文件的获取,如果加密的字节码则需要在该类中对文件进行解密
  • 传递的文件名是全限定性名称
  • 最好不要重写loadClass方法,容易破坏双亲委托模式
  • 不要把类放在AppClassLoader路径下,容易导致AppClassLoader类加载器加载
因JVM自带ClassLoader只懂从本地文件加载标准java cass文件,若编写自己的ClassLoader,需做到以下三点
  1. 执行非置信代码之前,自动验证数字签名
  2. 动态地创建符合用户特定需要的定制化构建类
  3. 从特定的场所所取得java class,如从数据库中和网络中

相关文章

  • JVM——类加载机制

    前言 今天就来介绍类的加载机制以及双亲委派机制。 JVM 类加载机制 JVM 类加载的五个阶段 JVM 类加载机制...

  • JVM类加载学习二-类加载机制学习

    JVM类加载机制 @(Java)[JVM|类文件结构] [TOC] 基本介绍 JVM的类加载机制:JVM把描述类的...

  • Java类加载机制

    类加载机制 在Java面试中类加载机制是十分常见的考察点,时常和JVM内存模型,JVM内存管理,反射等知识点穿插考...

  • JVM 知识梳理

    JVM 体系总体分四大块: 类的加载机制 jvm内存结构 GC算法 垃圾回收 GC分析 命令调优 1. 类的加载机...

  • JVM——类加载机制

    JVM类加载机制 JVM类加载机制是通过类加载器ClassLoader来将.class文件加载到内存中,以便调用....

  • jvm知识点总览

    jvm 总体梳理 jvm体系总体分四大块: 类的加载机制jvm内存结构GC算法 垃圾回收GC分析 命令调优 当然这...

  • JVM知识体系-01类加载机制

    一、思维导图 二、大纲 2.1 类的加载是什么? 将类(.class)文件中的二进制数据读入内存,并放在运行时数据...

  • 类加载机制(一)

    加载机制系列类加载机制(一)类加载机制(二)类加载机制(三) 类加载机制 1.JVM把class文件加载到内存,对...

  • JVM类加载机制

    JVM类加载机制 一、基础知识 类的加载过程可以分为如下三类: 1.1 加载 将字节码从不同的数据源读取到JVM中...

  • Java——JVM篇——收藏系列来啦(终结篇)

    转自:Java——JVM篇——收藏系列来啦(终结篇)侵删。 2.9.JVM 类加载机制 JVM 类加载机制分为五个...

网友评论

      本文标题:JVM知识体系-01类加载机制

      本文链接:https://www.haomeiwen.com/subject/wjshkdtx.html