虚拟机的类加载机制

简介: 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类即虚拟机的类加载机制.在Java中,类型的加载、链接和初始化过程都是在程序运行期间完成的如编写一个面向接口的应用程序,可等到运行时再指定其实际的实现类.

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

即虚拟机的类加载机制.

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

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

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

1 类加载的时机

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

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

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

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

  • 遇到new、getstatic(读取一个类的静态字段)、putstatic或invokestatic(设置一个类的静态字段)这4条指令的时候,如果类没有进行过初始化.则先触发其初始化
  • 使用反射包方法进行反射调用的时候,如果类未经初始化,则先触发其初始化.
  • 当初始化一个类的时,若其父类未经初始化,则先触发其父类的初始化.
  • JVM启动时,用户需指定一个主类,虚拟机会先初始化此类
  • 当使用JDK7的动态语言支持时,如果一个java.lang.invoke.MethodHandler实例最后的解析结果为REF_getStatic、REF_pusStatic、REF_invokeStatic的方法句柄(句柄中包含了对象的实例数据和类型数据,句柄是访问对象的一种方法.句柄存储在堆中),并且句柄对应的类没有被初始化,那么先触发其初始化

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

img_de8c5c637b587dcd11ee73560585f9f1.png

输出结果

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

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

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

img_e01de5c1f96a9839bb44d8eef57dd0f6.png

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

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

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


img_4a47b880eff99ebac123fbe8b12479c7.png

输出结果

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

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

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

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

  • 类初始化时,如果发现父类未经初始化,则先初始化父类,然后再初始化自己
  • 接口初始化时,并不要求父接口已经全部初始化,只有在运行过程中用到当父接口时(如引用接口中定义的常量)才初始化父接口
目录
相关文章
|
4月前
|
前端开发 安全 Java
聊聊Java虚拟机(一)—— 类加载子系统
虚拟机就是一款用来执行虚拟计算机指令的计算机软件。它相当于一台虚拟计算机。大体上,虚拟机分为系统虚拟机和程序虚拟机。系统虚拟机就相当于一台物理电脑,里面可以安装操作系统;程序虚拟机是为了执行单个计算机程序而设计出来的虚拟机。其中 Java 虚拟机就是**执行 Java 字节码指令的虚拟机**。
62 2
|
存储 安全 Java
《Java 虚拟机》 类加载阶段
《Java 虚拟机》 类加载阶段
217 0
《Java 虚拟机》 类加载阶段
|
Java
【Java 虚拟机原理】Java 类中的类加载初始化细节 ( 只使用类中的常量时加载类不会执行到 ‘初始化‘ 阶段 )
【Java 虚拟机原理】Java 类中的类加载初始化细节 ( 只使用类中的常量时加载类不会执行到 ‘初始化‘ 阶段 )
419 0
【Java 虚拟机原理】Java 类中的类加载初始化细节 ( 只使用类中的常量时加载类不会执行到 ‘初始化‘ 阶段 )
|
Java
【Java 虚拟机原理】Java 类加载过程 ( 加载 | 连接 - 验证 准备 解析 | 初始化 | 使用 | 卸载 )
【Java 虚拟机原理】Java 类加载过程 ( 加载 | 连接 - 验证 准备 解析 | 初始化 | 使用 | 卸载 )
126 0
【Java 虚拟机原理】Java 类加载过程 ( 加载 | 连接 - 验证 准备 解析 | 初始化 | 使用 | 卸载 )
|
存储 安全 前端开发
深入理解JVM虚拟机读书笔记——类的加载机制
注:本文参考自周志明老师的著作《深入理解Java虚拟机(第3版)》,相关电子书可以关注WX公众号,回复 001 获取。
深入理解JVM虚拟机读书笔记——类的加载机制
|
存储 前端开发 安全
|
Java
java 虚拟机内存划分,类加载过程以及对象的初始化
涉及关键词: 虚拟机运行时内存 java内存划分 类加载顺序  类加载时机  类加载步骤  对象初始化顺序  构造代码块顺序 构造方法 顺序 内存区域   java内存图  堆 方法区 虚拟机栈 本地方法栈 程序计数器  局部变量表   栈帧  java堆 运行时常量池   直接内存    本文从三个部分理解java的初始化 1).
1794 0
|
存储 安全 Java
[深入理解Java虚拟机]第七章 类加载的过程 接下来我们详细讲解一下Java虚拟机中类加载的全过程,也就是加载、验证、准备、解析和初始化这5个阶段所执行的具体动作。加载“加载”是“类加载”(C
 通过之前的介绍可知,类加载过程共有5个步骤,分别是:加载、验证、准备、解析、初始化。其中,验证、准备、解析称为连接。
1685 0
|
Java 前端开发
深入java虚拟机学习 -- 类的加载机制(四)
类加载的命名空间 每个类加载器都有自己的命名空间,命名空间由所有以此加载器为初始类加载器的类组成,不同命名空间的两个类是不可见的,但只要得到类所对应的Class对象的refrence(反射),还是可以访问另一个命名空间的类信息的。
1040 0
|
Java C++ 编译器
深入java虚拟机学习 -- 类的加载机制(三)
类的初始化时机 在上篇文章中讲到了类的六种主动使用方式,反射是其中的一种(Class.forName(“com.jack.test”)),这里需要注意一点:当调用ClasLoader类的loadClass方法对类进行加载的时候,并不是对类的主动调用,不会导致类的初始化。
981 0