java类加载过程

107 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情

image.png

1.这张图表现了一个类的生命周期,完整一点的话,我们可以在最开始加上javac编译阶段。而 "类加载"只包括加载、连接、初始化这三个过程。
2.需要区分“类加载”与“加载”,加载只是类加载的第一个环节。
3.解析部分是灵活的,它可以在初始化环节之后再进行,实现所谓的“后期绑定",这点后面在讲到解析环节时会详细讲。其他环节的顺序不可改变。

加载

加载是一个读取Class文件,将其转化为某种静态数据结构存储在方法区内,并在堆中生成一个便于用户调用的java.lang.Class类型的对象的过程。

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

image.png

验证

  1. 在加载阶段也有验证:文件格式验证
  2. 在连接-验证阶段:元数据、字节码验证(魔数Cafebabe就是在这时验证的)
  3. 在连接-解析阶段:符号引用验证

java之元数据( metadata)
元数据是指用来描述数据的数据,更通俗一点,就是描述代码间关系,或者代码与其他资源(例如数据库表)之间内在联系的数据。在一些技术框架,如struts、EJB、hibernate就不知不觉用到了元数据。
对struts来说,元数据指的是struts-config.xml;
对EJB来说,就是ejb-jar.xml和厂商自定义的xml文件;
对hibernate来说就是hbm文件。
以上阐述的几种元数据都是基于xml文件的或者其他形试的单独配置文件。这样表示有些不便之处。
一、与被描述的文件分离,不利于一致性的维护;
二、所有这样文件都是ASCII文件,没有显式的类型支持。
基于元数据的广泛应用,JDK5.0引入了Annotation的概念来描述元数据。在java中,元数据以标签的形式存在于java代码中,元数据标签的存在并不影响程序代码的编译和执行。

如何创建元数据?
JDK5.0出来后,java语言中就有了四种类型(TYPE),即类(class)、枚举(enum)、接口(interface)和注解(@interface),它们是处在同一级别的。java就是通过注解来表示元数据的。

准备

  1. 注意:方法区是抽象概念,jdk1.7之前用永久代实现,jdk1.8之后用元空间实现
  2. 准备阶段为静态变量(还没轮到成员变量)赋默认值,也就是为下面的b赋int类型的默认值0

image.png

解析

image.png

  1. 这个阶段就是将符号引用替换成直接引用

    • 举例:当一个java类被编译成class字节码,假如这是A类,并且A中引用了B类,那么在编译阶段,A是不知道B有没有被编译的,而且此时B还未被加载,所以A不知道B的实际地址,那么A怎么能找到B呢?此时在A的class文件中,将使用一个字符串来代替B的地址,这个S就是符号引用;在运行时,如果A发生了类加载,到解析阶段会发现B还未被加载,那么将会触发B的类加载,将B加载到虚拟机中,此时A中的符号引用将会被替换成B的实际地址。
    • 如果A调用的B是一个具体实现类,那么就称为静态解析,因为解析的目标类很明确
    • 而假如上层Java代码使用了多态,这里的B可能是一个抽象类或者接口,假设B有C和D两个实现类,此时B的具体实现并不明确,也就不知道替换哪一个具体实现类作为直接引用,等到运行过程中发生了调用,此时虚拟机栈中将得到具体的类型信息,这时候再进行解析,就能使用具体的直接引用替换符号引用,这就是为什么有时候解析阶段发生在初始化阶段之后,这也就是动态解析
  2. 当解析部分完成,意味着整个连接部分的完成,这也就是说外部加载的类已经成功引用程序中

初始化

简单来说就是把下面的b赋值1

image.png

  1. 判断代码中是否存在主动的资源初始化动作,如果有,那么执行。

    • 主动资源初始化动作:不是指构造函数,而是class层面(cinit),比如静态变量赋值、静态代码块的逻辑、成员变量赋值;只有显式使用new指令才会调用构造函数进行对象实例化(init)

    • java中cinit和init方法详解,可以看看这篇文章(转载,不合适的话会删掉):blog.csdn.net/hu853996234…

image.png