深入拆解 Java 虚拟机
郑雨迪
Oracle 高级研究员,计算机博士
87446 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 40 讲
模块四:黑科技 (3讲)
深入拆解 Java 虚拟机
15
15
1.0x
00:00/00:00
登录|注册

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

自定义类加载器
应用类加载器
扩展类加载器
启动类加载器
新建数组对LazyHolder的影响
验证加载和初始化顺序
JVM参数-verbose:class
类初始化触发情况
< clinit >方法执行
常量值字段赋值
解析
准备
验证
双亲委派模型
类加载器
创建类
查找字节流
实践
初始化
链接
加载
Java虚拟机加载类的过程

该思维导图由 AI 生成,仅供参考

听我的意大利同事说,他们那边有个习俗,就是父亲要帮儿子盖栋房子。
这事要放在以前还挺简单,亲朋好友搭把手,盖个小砖房就可以住人了。现在呢,整个过程要耗费好久的时间。首先你要请建筑师出个方案,然后去市政部门报备、验证,通过后才可以开始盖房子。盖好房子还要装修,之后才能住人。
盖房子这个事,和 Java 虚拟机中的类加载还是挺像的。从 class 文件到内存中的类,按先后顺序需要经过加载、链接以及初始化三大步骤。其中,链接过程中同样需要验证;而内存中的类没有经过初始化,同样不能使用。那么,是否所有的 Java 类都需要经过这几步呢?
我们知道 Java 语言的类型可以分为两大类:基本类型(primitive types)和引用类型(reference types)。在上一篇中,我已经详细介绍过了 Java 的基本类型,它们是由 Java 虚拟机预先定义好的。
至于另一大类引用类型,Java 将其细分为四种:类、接口、数组类和泛型参数。由于泛型参数会在编译过程中被擦除(我会在专栏的第二部分详细介绍),因此 Java 虚拟机实际上只有前三种。在类、接口和数组类中,数组类是由 Java 虚拟机直接生成的,其他两种则有对应的字节流。
说到字节流,最常见的形式要属由 Java 编译器生成的 class 文件。除此之外,我们也可以在程序内部直接生成,或者从网络中获取(例如网页中内嵌的小程序 Java applet)字节流。这些不同形式的字节流,都会被加载到 Java 虚拟机中,成为类或接口。为了叙述方便,下面我就用“类”来统称它们。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Java虚拟机加载Java类的过程类似于盖房子,需要经历加载、链接和初始化三大步骤。加载阶段通过类加载器查找字节流并创建类,类加载器按照双亲委派模型依次转发加载请求。链接阶段包括验证、准备和解析,确保类满足虚拟机约束条件并为静态字段分配内存。解析阶段将符号引用解析为实际引用,触发未加载类的加载。文章还介绍了Java类的分类和不同类加载器的职责,以及Java 9对类加载器的略微更改。总的来说,文章详细解释了Java虚拟机加载类的过程和相关概念,为读者深入理解Java类加载提供了全面的指导。 在Java代码中,静态字段的初始化可以通过直接赋值或静态代码块实现。类加载的最后一步是初始化,确保常量值字段被赋值,并执行< clinit >方法。类的初始化触发情况包括虚拟机启动、new指令、静态方法调用等。文章还提供了一个单例延迟初始化例子,说明了类初始化的线程安全特性。 总结来说,文章详细介绍了Java虚拟机将字节流转化为Java类的过程,包括加载、链接和初始化三大步骤。加载通过类加载器查找字节流并创建类,链接阶段确保类满足虚拟机约束条件,初始化阶段为常量值字段赋值并执行< clinit >方法。读者可以通过实践验证理论知识,例如使用JVM参数-verbose:class打印类加载顺序。整体而言,本文为读者提供了深入理解Java类加载的全面指南。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《深入拆解 Java 虚拟机》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(163)

  • 最新
  • 精选
  • 笨鸟
    1.虚拟机必须知道(加载)有这个类,才能创建这个类的数组(容器),但是这个类并没有被使用到(没有达到初始化的条件),所以不会初始化。 2.新建数组的时候并不是要使用这个类(只是定义了放这个类的容器),所以不会被链接,调用getInstance(false)的时候约等于告诉虚拟机,我要使用这个类了,你把这个类造好(链接),然后把static修饰的字符赋予变量(初始化)。 老师看看理解对不对,指点一下。

    作者回复: 对的!

    2018-10-18
    5
    112
  • 曲东方
    1. 新建数组会加载元素类LazyHolder;不会初始化元素类 2. 新建数组不会链接元素类LazyHolder;在getInstance(false)时才真正链接和初始化 ----------------- 链接的第一步:验证字节码,awk把字节码改为不符合jvm规范 初始化调用<clinit>(即class init) PS:好像二个问题包含了第一个问题的答案

    作者回复: 多谢指出!

    2018-07-25
    4
    95
  • mover
    到目前为止,讲解的内容没有超出周志明老师的 深入理解JAVA虚拟机这本书的内容,老师可以讲解的更深入一点吗?可以介绍一下类加载后在meta区的大概布局吗?class类对象与meta区的类数据结构是什么关系?当我们创建类,使用类时,类实例,类对象,meta区类数据结构是如何交互的?

    作者回复: 谢谢你的建议!前几章不好搞太难,希望后面能够满足你的需求

    2018-07-25
    2
    46
  • 韩恩同
    忍着瞌睡把内容看完了。 全是复习了一遍。 作者对 类加载中的 链接(验证、准备、解析)讲解不太到位吧? 另外,对一个的初始化发生在第一次主动使用该类时,作者列出的几种情况都属于主动使用类。感觉应该有被动使用的举例,并告知大家这样做是不会执行初始化的。

    作者回复: 多谢建议!

    2018-07-27
    3
    30
  • conce2018
    为什么叫双亲委派呀,明明只给了父类加载应该是单亲呀

    作者回复: 其实我也有这个疑问,英文中为parent不带s,照理应该翻译为单亲。但既然约定俗成翻译为双亲,就只好这样叫啦

    2018-09-25
    7
    27
  • 熊猫酒仙
    有几个疑问,请老师指点迷津。 1.扩展类加载器的父类,是启动类加载器,而后者是C++实现的,java继承C++的类?不大能理解。 2.虚方法的概念在C++中有了解过,java中的虚方法该如何定义呢?以前没接触过java虚方法的概念 3.我以前的理解是,有一个零值(0/null)初始化,针对于类的静态成员变量,如果是final修饰的静态成员变量,也就是常量,是初始化为代码中指定的值比如10。非final修饰的静态成员变量,在clint执行过程中赋值为代码中指定的值,请问老师是这样的吗?

    作者回复: 1. 可能我翻译得有点瑕疵,导致了你的误解。这里我指的是扩展器类的 父-类加载器,而不是父类-(加载器)。 2. Java中所有的非私有实例方法,都算是虚方法。调用这些方法的指令,也区分直接调用和虚调用。下一篇我会讲到。 3. 赞一个。被final修饰的静态成员变量,如果不是基本类型或者字符串,也会放在clinit 来做。

    2018-07-25
    5
    20
  • Skysper
    每次new一个类都是一次初始化吧?加载和链接以后生成的是什么样的数据结构?存储在什么地方?

    作者回复: 类的初始化只会发生一次,你可能指的是实例的初始化? JVM并不会直接使用.class文件,类加载链接的目的就是在JVM中创建相应的类结构,会存储在元空间(我之前用的老说法”方法区”,感谢某同学指出)。

    2018-07-25
    5
    18
  • Super丶X
    老师,你说可以通过不同的类加载器加载同一个类得到类的不同版本,我有个疑问,类是通过包名加类名来使用的,那怎么样区分不同的类加载器加载的类呢?

    作者回复: 你指的是在写代码的时候如何区分对吧?我认为没法区分。如果你有一个类的两个不同版本,而且它们不兼容,那么编译时指向哪个,就按哪个来编译。也就是说,如果要同时使用两个版本,那么你需要分开编译。

    2018-07-25
    2
    13
  • 小蛋壳
    加载阶段都加载哪些类呢,那么多类,全部加载吗?

    作者回复: 加载阶段是针对单个类的,一般用到的类才会被加载。大部分情况下,不同类的加载阶段是不同的。

    2018-07-26
    9
  • L.B.Q.Y
    从大的方面讲,类加载的结果是把一段字节流变换成Class结构并写方法区,实际写方法区具体是发生在加载、链接、初始化的哪个环节呢?

    作者回复: 在加载阶段就已经生成class结构了,所以我认为应该已经写入了方法区,只是被标记为未链接而暂不能使用。

    2018-07-25
    3
    8
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部