专栏首页国产程序员Java虚拟机是如何加载Java类的?

Java虚拟机是如何加载Java类的?

Java 虚拟机中的类加载即从 class 文件到内存中的类,按先后顺序需要经过加载、链接以及初始化三大步骤。

虚拟机的加载对象是什么?

上文中说过Java中有两种类型:基本类型和引用类型,而基本类型是由虚拟机预先定义好的,引用类型中的泛型参数又会在编译过程中被擦除,所以加载的对象就剩下类、接口和数组类。

在类、接口和数组类中,数组类是由 Java 虚拟机直接生成的,其他两种则有对应的字节流。无论是直接生成的数组类,还是加载的类,Java 虚拟机都需要对其进行链接和初始化。接下来,就详细介绍一下每个步骤具体都在干些什么。

虚拟机的加载流程是什么?

1.加载

是指查找字节流,并且据此创建类的过程。上面提过数组类是由Java虚拟机直接生成的,所以加载过程针对的是生成字节流的类与接口。如何找到这些字节流,则需要虚拟机借助类加载器。

启动类加载器是由 C++ 实现的,没有对应的 Java 对象,因此在 Java 中只能用 null 来指代。在 Java 9 之前,启动类加载器负责加载最为基础、最为重要的类,比如存放在 JRE 的 lib 目录下 jar 包中的类(以及由虚拟机参数 -Xbootclasspath 指定的类)。除了启动类加载器之外,另外两个重要的类加载器是扩展类加载器(extension class loader)和应用类加载器(application class loader),均由 Java 核心类库提供。故除了启动类加载器之外,其他的类加载器都是java.lang.ClassLoader 的子类,因此有对应的 Java 对象。

扩展类加载器的父类加载器是启动类加载器。它负责加载相对次要、但又通用的类,比如存放在 JRE 的 lib/ext 目录下 jar 包中的类(以及由系统变量 java.ext.dirs 指定的类)。

应用类加载器的父类加载器则是扩展类加载器。它负责加载应用程序路径下的类。(这里的应用程序路径,便是指虚拟机参数 -cp/-classpath、系统变量 java.class.path 或环境变量 CLASSPATH 所指定的路径。)默认情况下,应用程序中包含的类便是由应用类加载器加载的。

Java 9 引入了模块系统,并且略微更改了上述的类加载器1。扩展类加载器被改名为平台类加载器(platform class loader)。Java SE 中除了少数几个关键模块,比如说 java.base 是由启动类加载器加载之外,其他的模块均由平台类加载器所加载。当然还可以自定义类加载器哦。

除了加载功能之外,类加载器还提供了命名空间的作用,在 Java 虚拟机中,类的唯一性是由类加载器实例以及类的全名一同确定的。即便是同一串字节流,经由不同的类加载器加载,也会得到两个不同的类。在大型应用中,我们往往借助这一特性,来运行同一个类的不同版本。

2.链接

是指将创建成的类合并至 Java 虚拟机中,使之能够执行的过程。它可分为验证、准备以及解析三个阶段。

  • 验证阶段:确保被加载类能够满足 Java 虚拟机的约束条件。
  • 准备阶段:为被加载类的静态字段分配内存,构造其他跟类层次相关的数据结构。
  • 解析阶段:将符号引用解析成为实际引用(Java 虚拟机规范并没有要求在链接过程中完成解析。它仅规定了:如果某些字节码使用了符号引用,那么在执行这些字节码之前,需要完成对这些符号引用的解析)。

符号引用则是在 class 文件被加载至 Java 虚拟机之前,类无法知道其他类及其方法、字段所对应的具体地址,甚至不知道自己方法、字段的地址。每当需要引用这些成员时,Java 编译器会生成一个符号引用。在运行阶段,这个符号引用一般都能够无歧义地定位到具体目标上。

3.初始化

初始化即给常量赋值以及执行 < clinit > 方法的过程,完成之后,类才正式成为可执行的状态。

类初始化触发条件

  • 当虚拟机启动时,初始化用户指定的主类;
  • 当遇到用以新建目标类实例的 new 指令时,初始化 new 指令的目标类;
  • 当遇到调用静态方法的指令时,初始化该静态方法所在的类;
  • 当遇到访问静态字段的指令时,初始化该静态字段所在的类;
  • 子类的初始化会触发父类的初始化;
  • 如果一个接口定义了 default 方法,那么直接实现或者间接实现该接口的类的初始化,会触发该接口的初始化;
  • 使用反射 API 对某个类进行反射调用时,初始化这个类;
  • 当初次调用 MethodHandle 实例时,初始化该 MethodHandle 指向的方法所在的类。

总结

虚拟机加载Java类是Java 虚拟机将字节流转化为 Java 类的过程。这个过程可分为加载、链接以及初始化三大步骤。

  • 加载:是指查找字节流,并且据此创建类的过程。加载需要借助类加载器,在 Java 虚拟机中,类加载器使用了双亲委派模型,即接收到加载请求时,会先将请求转发给父类加载器。
  • 链接:是指将创建成的类合并至 Java 虚拟机中,使之能够执行的过程。链接还分验证、准备和解析三个阶段。其中,解析阶段为非必须的。
  • 初始化:是为标记为常量值的字段赋值,以及执行 < clinit > 方法的过程。类的初始化仅会被执行一次,这个特性被用来实现单例的延迟初始化。

本文分享自微信公众号 - 国产程序员(Monday_lida),作者:Monday

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 类加载过程是怎样的

    它是Java将字节码数据从不同的数据源读取到JVM中,并映射为JVM认可的数据结构(Class对象)。这里的数据源可能是各种各样的形态,如jar文件、class...

    一觉睡到小时候
  • JVM学习(一)

    程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里字...

    一觉睡到小时候
  • Java基础

    Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计语言和Java平台的总称。由James Gosling和同事们共同...

    一觉睡到小时候
  • 类加载机制

    学习本章前我们要对类文件结构有一个简单的认识,而学习类文件结构没有任何难度,更多的是参考《Java虚拟机规范》、《Java语言规范》中定义的规则。我们要对cla...

    胖虎
  • 技术转载——JVM里面Java类的生命周期,一篇搞定

    写在前面:2020年面试必备的Java后端进阶面试题总结了一份复习指南在Github上,内容详细,图文并茂,有需要学习的朋友可以Star一下! GitHub地...

    用户5546570
  • 深入Java虚拟机|类加载机制

    类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。它们开始的顺序如下图所示:

    技术从心
  • 面试官:请你谈谈 Java 的类加载过程

    刚刚走出校门的应届毕业生,如果在去寻求一份 Java 开发的工作时,你的面试官很有可能一边看着你的简历,一边漫不经心地问你:了解过 Java 类的加载过程吗?

    CG国斌
  • 类加载子系统 Krains 2020-07-31

    类加载子系统(ClassLoader)只负责从文件系统或者网络中加载class文件,至于它是否可以运行,则由执行引擎(Execution Engine)决定。

    Krains
  • 一张图看懂JVM之类装载系统

    与C/C++那些需要在编译器期进行连接工作的语言不同,Java类的加载、连接和初始化都是在程序运行时完成的,只有在类被需要的时候才进行动态加载,这种方式被称为“...

    用户5927304
  • JVM 类加载机制详解

    jvm将class文读取到内存中,经过对class文件的校验、转换解析、初始化最终在jvm的heap和方法区分配内存形成可以被jvm直接使用的类型的过程。

    用户1637228

扫码关注云+社区

领取腾讯云代金券