手记

虚拟机的类加载机制

虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类

即虚拟机的类加载机制.

在Java中,类型的加载、链接和初始化过程都是在程序运行期间完成的

如编写一个面向接口的应用程序,可等到运行时再指定其实际的实现类.
这种策略虽然会令类加载时增加一些性能开销,但是会为Java应用程序提供高度的灵活性.

Java天生的可以动态扩展的语言特性就是依赖运行期动态加载和动态链接

1 类加载的时机

类从被加载至内存到卸载出内存的生命周期


其中加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的.
而解析阶段可能会在初始化阶段后再开始.

虽然上述的5个阶段可能按序,但是并不是说一个接一个阶段完成后才开始,一个阶段的进行完全可能激活另一个阶段的进行,交叉混合式的进行

什么情况下需要开始类加载过程的第一个阶段-加载呢?
并无强制规范,但对于初始化阶段,有且只有以下5种情况必须立即对类进行初始化

  • 遇到new、getstatic(读取一个类的静态字段)、putstatic或invokestatic(设置一个类的静态字段)这4条指令的时候,如果类没有进行过初始化.则先触发其初始化

  • 使用反射包方法进行反射调用的时候,如果类未经初始化,则先触发其初始化.

  • 当初始化一个类的时,若其父类未经初始化,则先触发其父类的初始化.

  • JVM启动时,用户需指定一个主类,虚拟机会先初始化此类

  • 当使用JDK7的动态语言支持时,如果一个java.lang.invoke.MethodHandler实例最后的解析结果为REF_getStatic、REF_pusStatic、REF_invokeStatic的方法句柄(句柄中包含了对象的实例数据和类型数据,句柄是访问对象的一种方法.句柄存储在堆中),并且句柄对应的类没有被初始化,那么先触发其初始化

这5种行为称为对一个类进行主动引用,此外的所有引用类的方式都不会触发初始化,称为 被动引用


输出结果


初始化父类!
666
  • 原因分析
    本示例看似满足初始化时机的第一条:当要获取某一个类的静态字段的时候若该类尚未初始化,则对该类进行初始化

但由于这个静态成员变量属于父类,子类只是间接调用父类中的静态字段,因此子类调用value属于间接引用,而父类调用value属于直接引用

对于静态字段,只有直接定义这个字段的类才会被初始化,通过其子类引用父类中定义的静态字段,只会触发父类的初始化!


并没有输出”初始化父类!”


  • 原因分析
    这个过程看似满足初始化时机的第一条:遇到new创建对象时若类没被初始化,则初始化该类

但现在通过new要创建的是一个数组对象,而非SuperClass类对象,因此也属于间接引用,不会初始化SuperClass类



输出结果

hello world
  • 原因分析
    本示例看似满足类初始化时机的第一个条件:获取一个类静态成员变量的时候若类尚未初始化则初始化类.

但ConstClass类的静态字段被final修饰,是一个常量
被final修饰的常量在Java代码编译的过程中就会被放入它被引用的class文件的常量池中(这里是NotInitialization的常量池).
所以程序在运行期间如果需要调用这个常量,直接去当前类的常量池中取,而不需要初始化这个类

实际上,NotInitialization的Class文件中并无ConstClass类的符号的入口,这俩类在编译成Class之后就不存在任何联系了

接口和类都需要初始化,接口和类的初始化过程基本一样,有所区别的是前面说的5种情景的第三条

  • 类初始化时,如果发现父类未经初始化,则先初始化父类,然后再初始化自己

  • 接口初始化时,并不要求父接口已经全部初始化,只有在运行过程中用到当父接口时(如引用接口中定义的常量)才初始化父接口



作者:芥末无疆sss
链接:https://www.jianshu.com/p/75876562c06e
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。


0人推荐
随时随地看视频
慕课网APP