• 【java】JVM类加载机制


    JVM类加载机制


    类加载过程

    加载
    验证
    准备
    解析
    初始化
    使用
    卸载

    类加载详细过程

    1. 加载:

      1. )通过类的全限定名获取该类的二进制字节流

      2. )将二进制流所代表的静态结构转化为方法区的运行时数据结构

      3. )在内存中创建一个代表该类的java.lang.Class对象,作为方法区中这个类的各种数据的访问入口

    2. 验证:

      1. )文件格式,字节流是否符合class文件格式的规范,并且能被当前版本的虚拟机处理
      2. )元数据验证 ,对字节码描述信息进行语义分析,确保其符合java语法规范
      3. )字节码验证 对方法体进行语义分析,保证方法在运行时不会对虚拟机造成危害
      4. )符号引用验证,在解析阶段,确保解析正常执行
    3. 准备:正式为类变量(或者静态成员变量)分配内存并设置初始值,这些变量(不包括实例变量)所使用的内存都在方法区进行分配

    4. 解析:虚拟机将常量池中的符号引用替换为直接引用

    5. 初始化:执行类构造器()方法的过程

      ()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块中语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的。

      5种情况会触发初始化:

      1)遇到new、getstatic、putstatic、invokestatic这四条季节吗指令的时候,如果类没有经过初始化,则需要触发类的初始化。场景是:new实例化对象、设置类的静态字段、读取类的静态字段、调用类的静态方法。

      2)使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要触发其初始化

      3)当初始化一个类的时候,发现其父类还没有进行过初始化,则需要先触发其父类的初始化

      4)当虚拟机启动的时候,用户需要指定一个要执行的主类,即包含main方法的类,虚拟机会先初始化这个主类

      5)当使用jdk1.7动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果是 REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且这个方法句柄对应的类没有进行初始化,则主要触发其初始化

    类加载器

    任意一个类 + 加载它的类加载器 ==》其在 Java 虚拟机中的唯一性;每一个类加载器,都有一个独立的类名称空间。

    比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个 Class 文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那么这两个类就必定不相等。

    这里的“相等”,包括代表类的 Class 对象的 equals() 方法、isInstance() 方法的返回结果,也包括使用 instanceof 关键字做对象所属关系判定等情况。

    加载器种类

    • 启动类加载器:负责将存放在 \lib 目录中的,并且能被虚拟机识别的(仅按照文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录中也不会被加载)类库加载到虚拟机内存中。
    • 扩展类加载器:负责加载 \lib\ext 目录中的所有类库,开发者可以直接使用扩展类加载器。
    • 应用程序类加载器:由于这个类加载器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,所以一般也称它为“系统类加载器”。它负责加载用户类路径(classpath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
    启动类加载器
    扩展类加载器
    应用程序类加载器
    虚拟机层面
    1.bootstrap class loader
    2.继承自java.lang.ClassLoader的其他类加载器
    开发角度细分三层类加载器
    自定义类加载器1
    自定义类加载器2

    双亲委派模型

    双亲委派模型是描述类加载器之间的层次关系。它要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。(父子关系一般不会以继承的关系实现,而是以组合关系来复用父加载器的代码)

    无法完成
    无法完成
    类加载请求
    类加载器
    父类加载器
    顶层的启动类加载器
    • 工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(找不到所需的类)时,子加载器才会尝试自己去加载。(在 java.lang.ClassLoader 中的 loadClass() 方法中实现该过程。)
    • 原因:像 java.lang.Object 这些存放在 rt.jar 中的类,无论使用哪个类加载器加载,最终都会委派给最顶端的启动类加载器加载,从而使得不同加载器加载的 Object 类都是同一个。相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为 java.lang.Object 的类,并放在 classpath 下,那么系统将会出现多个不同的 Object 类,Java 类型体系中最基础的行为也就无法保证。
  • 相关阅读:
    【c++刷题Day2】专题3栈与队列&单调栈与单调队列
    【OpenCV 例程 300篇】245. 特征检测之 BRISK 算子
    git远程创建了分支,本地如何更新到最新的分支
    办公技巧:Excel日常高频使用技巧
    垃圾判断算法与4大引用
    计算机毕业设计Python+django的基于协同过滤算法的电影推荐系统(源码+系统+mysql数据库+Lw文档)
    使用51单片机控制T0和T1分别间隔1秒2秒亮灭逻辑
    HashMap JDK1.7与1.8的区别
    JS-(13) 事件(二)
    【工具】创客贴会员|创客贴截止2024年6月所有AI功能效果实测(热门推荐和图片编辑部分)
  • 原文地址:https://blog.csdn.net/Wang_Dong_Liang/article/details/127119040