spice and wolfspice and wolf Be the One you wanna Be

类加载详解

类加载是将class文件加载到java虚拟机的过程。更详细明确的加载过程参考这里

类加载时机

  • 遇到new(new对象)、getstatic、putstatic、invokestatic(静态成员和静态方法的访问)这四条指令时,如果对应的类没有初始化,则要对对应的类先进行初始化。
  • 是用java.lang.reflect对类进行反射调用时。
    Class c = Class.forname("com.spiceandwolf.horo");
  • 初始化一个类,当发现其父类还没有初始化时,需要先初始化其父类。
  • 当虚拟机开始启动时,用户需要指定一个主类(main),虚拟机会先执行这个主类的初始化。

类加载的过程

  • 加载。通过类加载器基于class文件创建class对象。数组类不通过类加载器创建,而是由java虚拟机直接创建,但是数组类和类加载器有很密切的关系,因为数组类的元素类型最终需要靠类加载器创建。
    1. 将文件转换为二进制字节流
    2. 字节流的二进制存储结构转换为堆(1.7之前方法区)中运行时数据结构
    3. 创建java.lang.class对象
  • 验证。保证二进制字节流中的信息符合虚拟机规范,没有安全问题。
  • 准备。为类的静态成员变量分配内存并初始化初始值。
  • 解析。将常量池的符号引用替换为直接引用的过程。主要针对类、接口、字段、类方法和接口方法四类符号引用进行替换,分别对应于常量池中的constant_class_info、constant_fieldref_info、constant_methodref_info、constant_interfaceref_info四种常量类型。
    • 类或接口的解析。判断所要转换的直接引用是数组类型,还是普通的对象类型的引用,从而进行不同的解析。
    • 字段解析。
      1. 会在本类中查找是否有简单名称与字段描述符都匹配的字段。
      2. 如果有,则查找结束。
      3. 如果没有,则会按照继承关系从上往下递归搜索该类所实现的各个接口和他们的父接口。
      4. 如果没有,则按照继承关系从上往下递归搜索其父类,直至查找结束。
    • 类方法解析。对类方法的解析与对字段解析的搜索步骤差不多,只是多了判断该方法所处的是类还是方法的步骤,对类方法的匹配搜索,是先搜索父类再搜索接口。
    • 接口方法解析。与类方法解析步骤类似,只是搜索的是接口不会有父类。
  • 初始化。调用<clinit>方法。
    • <clinit>方法是编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并而产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的。
    • 静态代码块只能访问到出现在静态代码块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但不能访问。
    • 实例构造器<init>会显式调用父类构造函数,而类的<clinit>不需要调用父类的类构造函数,虚拟机会确保子类的<clinit>方法执行前已经执行完父类的<clinit>方法,因此在JVM中第一个执行<clinit>方法的类一定是java.lang.Object。
    • 如果一个类/接口中没有静态代码块,也没有静态成员变量的赋值操作,那么编译器就不会为此类生成<clinit>方法。
    • 接口也需要通过<clinit>方法为接口中定义的静态成员变量显式初始化。
      接口中不能使用静态代码块,但仍然有变量初始化的赋值操作,因此接口与类一样会生成<clinit>方法,不能的是,执行接口的<clinit>方法不需要先执行父接口的<clinit>方法,只有在父接口中的静态成员变量被使用到时才会执行父接口的<clinit>方法。
    • 虚拟机会保证多线程环境中一个类的<clinit>方法被正确的加锁。

类加载器

类加载器及作用

双亲委派机制

破坏双亲委派机制

  • 第一次破坏。双亲委派机制是jdk1.2后加入的,并且双亲委派机制的逻辑编写在loadClass方法中,如果重写了loadClass方法,则会破坏双亲委派机制。所以jdk1.2后正确实现自定义类加载器的方法是重写findClass方法。
  • 第二次破坏。SPI机制
  • 第三次破坏。热部署OSGi

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Press ESC to close