类的加载机制

作者: 金馆长说 | 来源:发表于2018-07-23 22:00 被阅读8次

    类不被初始化的情况

    1. 对于静态字段,只有直接定义这个字段的类才会变初始化,子类引用父类的静态字段不会导致子类的初始化,只会初始化父类。
    public class SuperClass {
        static {
            System.out.println("SuperClass init!");
        }
        public static int value = 123;
    
    
    }
    
    public class SubClass extends SuperClass {
        public static int aa=32;
        static {
            System.out.println("SubClass init!");
        }
    }
    
    
    1. 数组的定义不会导致类的初始化
     SuperClass[] sca = new SuperClass[10];
    
    1. 使用静态常量不会导致类的初始化,因为常量在编译阶段就会存入调用类的常量池,本质上没有引用到定义类的常量,类在被编译成class之后,静态常量和类就没有关系了。
    
    public class ConstClass {
    
        static {
            System.out.println("ConstClass init!");
        }
    
        public static final String HELLOWORLD = "hello world";
    
    }
    
    
     public class Test {
    
        @org.junit.Test
        public void test() {
            System.out.println(ConstClass.HELLOWORLD);
        }
    
    }  
    

    类加载过程7阶段

    加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这个顺序按部就班的执行。

    何时会执行加载的第一个过程?
    这么五种情况是肯定会执行的

    1. 在遇到new 、读取或者设置一个类的静态字段(final除外,final在编译期间就放入了常量池)的时候,以及调用一个类的静态方法的时候。
    2. 使用反射调用的时候,如何类没有被初始化则会先触发其初始化。
    3. 类没有没有初始化的时候,如果其父类没有初始化。则需要先触发其父类的初始化。
    4. 执行一个main()方法
    5. 当使用JDK 1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。


      类的加载过程7个阶段

    1. 加载

    • 加载是类加载的过程的一个阶段,加载到我们的JVM需要需要经过三个过程
    1. 通过一个类名来获取到此类的二进制字节流
    2. 将字节流的静态存储结构转化为方法区运行时的数据结构
    3. 在内存中生成这个类的 Class对象,作为方法区的访问入口。


      JVM加载类图示过程
    几种类加载器

    2. 验证

    • 验证这个步骤按照深入JAVA虚拟机一书描述的是,很重要的一个步骤。因为这个步骤需要保证,加载进来的Class文件是符合虚拟机规范的,并且不会对虚拟机造成安全危害,这个阶段只要有以下三个操作。
    1. 文件格式验证
      这个步骤主要是检查字节流是否符合Class文件格式规范,是否以魔数0xCAFEBABE开头,版本号,常量问题。

    下面是一个class得二进制字节码,cafe开头的就是魔数,它是JAVA之父自己定义的一个规则,CafeBabe代表咖啡宝贝,这个也符号Java的咖啡标志。


    魔数
    1. 元数据验证
      这个阶段只要是对字节码进行语义分析,以保证字节码符合Java语义的规范,可能包含的检查有这个class是否有父类,是否集成了不允许继承的类比如final修饰的类,是否实现了父类要求实现的接口,字段是否产生了矛盾。

    2. 字节码验证
      这阶段是验证中最复杂的一步,主要是确定class的语义是否合法、符合逻辑,对类的方法体进行验证分析,保证类的方法不会对虚拟机安全造成危害。

    3. 符号引用验证
      这个阶段主要是检查class的一些访问修饰符是否符合规范,符号引用验证的目的是确保解析动作能正常执行。

    3. 准备

    准备阶段是正式为类变量分配内存并设置类初始值的阶段,分配static修饰的变量,这些变量所使用的内存都将在方法区中分配。

      public static int value = 123
    

    value变量在被分配的时候,它的值不会是123而是默认的0。因为这个过程还未执行到Java代码,赋值为123的操作将在初始化阶段完成。

    4. 解析

    • 解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,
    1. 符号引用以一组符号来描述所引用的目标,它可以是新形势的字面量。
    2. 直接引用是指向目标的指针、相对偏移量或者一个能直接定位到目标的句柄。

    解析类型主要有

    1. 类接口的解析
    2. 字段解析
    3. 类方法解析
    4. 接口方法解析

    5. 初始化

    类初始化是类加载过程中的最后一步,到了初始化阶段虚拟机才真正开始执行Java程序代码,准备阶段变量已经赋值过系统要求的初始值,而初始化阶段则回去初始化class的类变量和其他资源。

    6. 使用

    到了使用这一步就说明class已经正确的执行完了类加载的过程,class已经被加载到了虚拟机中,能被Java代码正确调用的过程。

    7. 卸载

    该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例。

    • 加载该类的ClassLoader已经被GC。
    • 该类的java.lang.Class 对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法

    相关文章

      网友评论

        本文标题:类的加载机制

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