zoukankan      html  css  js  c++  java
  • Java类加载机制

    Java类加载机制

    简介

    类的加载:
    将类的.class文件中的二进制数据读取到内存中,将其放在运行时数据区的方法区中,在堆内存中创建一个java.lang.Class对象,用来封装在方法区内的数据结构。

    Class对象:
    封装类在方法区内的数据结构,提供访问方法区的数据结构的接口。

    注:不需要等到某个类被“首次使用”再加载,允许预先加载。

    加载.class文件的方式:

    • 本地系统中直接加载
    • zip,jar文件中加载
    • Java源文件动态编译

    类的生命周期

    类加载的过程:

    1. 加载(loading)
    2. 连接
      • 验证(verification)
      • 准备(preparation)
      • 解析(resolution)
    3. 初始化(initialization)
    4. 使用(using)
    5. 卸载(unloading)

    注:

    • 加载,验证,准备和初始化发生的顺序是确定的,而解析阶段则不一定。
    • 解析可能发生在初始化之后(运行时绑定/动态绑定/晚期绑定)
    • 这些阶段的先后顺序是开始的时机,不代表运行或完成的时机(有可能先开始的后完成)。

    加载

    查找并加载类的二进制数据。

    功能:

    • 通过类的全限定名来获取其定义的二进制字节流。
    • 将字节流代表的静态存储结构转换为方法区的运行时数据机构。
    • 在堆内存中生成一个代表该类的java.lang.Class对象,作为该数据的访问入口。

    连接

    验证

    确保被加载的类的正确性。(非必要)

    行为

    • 文件格式验证:验证字节流是否符合Class文件格式的规范。
    • 元数据验证:对字节码描述的信息进行语义分析。
    • 字节码验证:通过数据流和控制流分子,确定程序语义是否合法,符合逻辑。
    • 符号引用验证:确保解析动作正确执行。

    准备

    为类的静态变量分配内存(方法区),并将其初始化为默认值。

    注意

    • 内存分配只包括类变量(static)(实例变量:对象实例化随着对象分配在堆内存中).
    • 设置的初始值为数据类型默认的零值.
    • 若类字段的字段属性为static final,则被初始化为所指定的值,而非默认零值.
    • 对于基本数据类型:类变量(static)和全局变量,若不显式赋值则默认为零值.
    • 对于局部变量:使用前必须显式赋值,否则无法通过.
    • static final修饰的常量必须在声明时赋值;final修饰的变量可以声明时赋值,也可以类初始化显式赋值.
    • 引用类型未显式赋值,则默认为null.
    • 数组初始化为赋值,则其中每个元素赋值对应数据类型的零值.

    解析

    把常量池内的符号引用替换为直接引用的过程.

    针对:类或接口,字段,类方法,接口符号,方法类型,方法句柄和调用点限定符.

    符号引用:一组符号来描述目标,可以是任何字面量.

    直接引用:直接指向目标的指针,相对偏移量或间接定位到目标的句柄.

    初始化

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

    设定方式:

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

    初始化步骤

    • 若类没有被加载和连接,则先加载并连接该类.
    • 若类的直接父类没有被初始化,则先初始化其直接父类.
    • 若类中有初始化语句,则依次执行初始化语句.

    初始化时机

    类被主动使用时导致类的初始化.

    • 创建类的实例(new Object()).
    • 访问类或接口的静态变量.
    • 调用类的静态方法.
    • 反射(Class.forName("io.github.truestoriesavici01.Test"))
    • 初始化某个类的子类,其父类也被初始化.
    • 虚拟机启动时被表明为启动类的类.

    结束

    • 执行System.exit()方法.
    • 程序正常结束.
    • 程序执行时遇到异常或错误而终止.
    • 操作系统导致的虚拟机进程终止.

    类加载器

    分类

    • 启动类加载器(Bootstrap ClassLoader):负责加载lib目录或指定的可识别的类库,无法被直接引用.
    • 扩展类加载器(Extension ClassLoader):由ExtClassLoader实现,加载ext目录或指定的类库,可直接使用.
    • 应用程序类加载器(Application ClassLoader):由AppClassLoader实现,负责加载用户类路径(ClassPath)指定的类.

    注:三种类加载器相互配合进行加载的.

    JVM类加载机制

    • 全盘负责:当一个类加载器负责加载某个Class时,该Class所依赖或引用的其他Class也将由这个类加载器负责载入.
    • 父类委托:先让父类加载器试图加载该类,若父类加载器无法加载该类时才试图从自己的类路径中加载该类.
    • 缓存机制:所有加载过的Class都会被缓存.当程序使用Class时,先从缓存中寻找该Class.若不存在,才读取该类对应的二进制数据,转换为Class对象,存入缓存中.

    类的加载方式

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

    Class.forName()ClassLoader.loadClass()的区别

    • Class.forName():将类的.class文件加载到JVM中,会类进行解释,执行类中的static块.
    • ClassLoader.loadClass():将.class文件加载到JVM中.

    双亲委派模型

    简介

    当一个类加载器收到类加载请求时,不会自己加载该类,而是委托父加载器.依次向上发出请求,直到传递到顶层的启动类加载器.只有当父加载器没有找到所需的类,无法完成加载时,子加载器才试图自己加载该类.

    流程

    1. 当AppClassLoader加载一个class时,将类加载请求委派给父类加载器ExtClassLoader去完成.
    2. 当ExtClassLoader加载一个class时,先将类加载请求委托给BootStrapClassLoader去完成.
    3. 若BootStrapClassLoader加载失败,会使用ExtClassLoader尝试加载.
    4. 若ExtClassLoader也加载失败,则使用AppClassLoader加载,若加载失败,会报出异常ClassNotFountException.

    优势

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

    自定义类加载器

    • 继承自ClassLoader类.
    • 重写findClass方法.

    参考:

  • 相关阅读:
    linux 创建python虚拟环境
    dic1.update(dic2)和{**dic2, **dic1}的区别
    redis
    电脑开机一卡一卡的,重启就好了
    股东大会和董事会的区别
    word空白框打钩
    计划资产回报
    成本法为什么要转权益法
    租赁负债
    AIDA64序列号
  • 原文地址:https://www.cnblogs.com/truestoriesavici01/p/13217311.html
Copyright © 2011-2022 走看看