当前位置: 首页 > news >正文

【03】Java虚拟机是如何加载Java类的

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

一、加载

加载就是查找字节流,并且据此创建类的过程。

除了启动类加载器(所有类加载器的祖师爷,由C++实现,没有对应的Java对象)之外,其他的类加载器都是 java.lang.ClassLoader 的子类。这些类加载器需要先由另一个类加载器,比如说启动类加载器加载至JVM中,才能进行类加载。

双亲委派模型:每当一个类加载器接收到加载请求时,它会先将请求转发给父类加载器,在父类加载器没有找到所请求的类的情况下,该类加载器才会尝试去加载。

Java9之前:
启动类加载器 :加载最基础和最重要的类(JRE的lib目录下jar包中的类)。
扩展类加载器 :其父类加载器是启动类加载器,负责加载相对次要,但通用的类(JRE的lib/ext目录下jar包中的类)。
应用类加载器:其父类加载器是扩展类加载器,负责加载应用程序路径下的类(这里的应用程序路径,是指虚拟机参数 -cp/-classpath、系统变量Java.class.path或环境变量CLASSPATH所指定的路径)默认情况下,应用程序中包含的类便是由应用类加载器加载的。

Java9之后:引入模块系统
扩展类加载器 rename为 平台类加载器

除了由 Java 核心类库提供的类加载器外,我们还可以加入自定义的类加载器,实现特殊的加载方式,eg 可以对class文件进行加密,加载时再利用自定义的类加载器对其解密。

在JVM中,类的唯一性是由类加载器实例以及类的全名一同确认。即使是同一串字节类,经由不同类加载器加载,也会得到两个不同的类。

二、链接

链接,是指将创建成的类合并至JVM中,使之能够执行的过程,分为三个如下阶段

Created with Raphaël 2.3.0 验证(确保被加载类能满足JVM的约束条件) 准备(为被加载类的静态字段分配内存) 解析(将符号引用解析成为实际引用)

注意:在class文件被加载至JVM之前,该类无法知道其他类及方法、字段对应的具体地址,甚至不知道自己方法、字段的地址,因此,每当需要引用这些成员时,Java编译器会生成一个符号引用(java编译器会暂时使用符号引用表表示目标方法),在运行阶段,该符号引用一般能无歧义的定位到具体目标,解析的目的就是将这些符号引用解析成为实际引用
当然,这些符号引用有可能指向一个未被加载的类或类的字段,那么解析也将触发这个类的加载(但未必触发这个类的链接及初始化)

1.非接口符号引用

假定该符号引用指向C类

Created with Raphaël 2.3.0 在C中查找符合名字及描述符的方法 未找到,则找C父类直到Object类 未找到,则找C直接或间接实现的接口中搜索
2.接口符号引用

假定该符号引用指向接口I

Created with Raphaël 2.3.0 在I中查找符合名字及描述符的方法 未找到,在Object类中的公有实例方法中搜索 未找到,则在I的超接口中搜索

经过上述解析步骤后,符号引用会被解析成实际引用。
对于静态绑定的方法调用而言,实际引用是一个指向方法的指针;对于动态绑定的方法调用而言,实际引用是一个方法表的索引

三、初始化

只有当初始化完成以后,类才正式成为可执行的状态。

JVM进行类的初始化之前,先简单了解下Java编译器都做了什么工作。
对于Java中初始化,静态字段的初始化有点特殊,
两种方式初始化一个静态字段,
(1)声明时直接赋值(若直接赋值的静态字段被final所修饰,且它的类型是基本类型或字符串时,该字段便会被编译器标记成常量,其初始化直接由JVM完成)
(2)在静态代码块中对其赋值。

所以除了对于常量是直接由JVM完成,其余的初始化(直接赋值操作及所有静态代码块中的代码),会被Java编译器置于同一个方法中,并把它命名为***< clinit >***。

总结为一句话就是,类加载的最后一步初始化,便是为标记为常量 的字段赋值以及执行***< clinit >***方法的过程。

类的初始化仅会被执行一次,这个特性被用来实现单例的延迟初始化。如下demo

public class Singleton {private Singleton() {}private static class LazyHolder {static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return LazyHolder.INSTANCE;}
}

这段代码是著名的单例延迟初始化例子,只有当调用Singleton.getInstance 时,程序才会访问LazyHolder.INSTANCE,才会触发对 LazyHolder 的初始化(对应下图第 4 种情况),继而新建一个 Singleton 的实例

![image.png-74.8kB][1]

个人思考

public class Singleton {private Singleton() {}private static class LazyHolder {static final Singleton INSTANCE = new Singleton();static {System.out.println("LazyHolder.<clinit>");}}public static Object getInstance(boolean flag) {if (flag) return new LazyHolder[2];return LazyHolder.INSTANCE;}public static void main(String[] args) {getInstance(true);System.out.println("----");getInstance(false);}
}

1.新建数据(10行)会导致LazyHolder的加在吗?会初始化吗?
2.新建数组会导致LazyHolder的链接吗?
答案:
1.虚拟机必须知道(加载)有这个类,才能创建这个类的数组(容器),但是这个类并没有被使用到(没有达到初始化的条件),所以不会初始化。所以新建数组会加载元素类LazyHolder;不会初始化元素类
2.新建数组的时候并不是要使用这个类(只是定义了放这个类的容器),所以不会被链接,调用getInstance(false)的时候约等于告诉虚拟机,我要使用这个类了,你把这个类造好(链接),然后把static修饰的字符赋予变量(初始化)。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • AttributeError: module ‘selenium.webdriver‘ has no attribute ‘PhantomJS‘
  • QT 关于QTableWidget的常规使用
  • Postman测试工具详细解读
  • 如何将整个运行环境打包成docker
  • 每日一知识点 - Java Lambda 表达式
  • C++——类和对象(中)
  • DeFi革命:揭秘去中心化金融的核心技术与实操指南
  • Typesript的type和interface的异同?
  • vscode回退不显示了,不方便操作
  • Rust:cargo的常用命令
  • Flutter Geolocator插件使用指南:获取和监听地理位置
  • 乐鑫ESP32-H2设备联网芯片,集成多种安全功能方案,启明云端乐鑫代理商
  • 【QT】TCP
  • Qt自定义带前后缀图标的PushButton
  • 添加sidecar容器并输出日志
  • (ckeditor+ckfinder用法)Jquery,js获取ckeditor值
  • 【翻译】Mashape是如何管理15000个API和微服务的(三)
  • Android系统模拟器绘制实现概述
  • bearychat的java client
  • ComponentOne 2017 V2版本正式发布
  • css布局,左右固定中间自适应实现
  • ES10 特性的完整指南
  • HTML-表单
  • JavaScript 无符号位移运算符 三个大于号 的使用方法
  • Median of Two Sorted Arrays
  • Nacos系列:Nacos的Java SDK使用
  • Nodejs和JavaWeb协助开发
  • react 代码优化(一) ——事件处理
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 从地狱到天堂,Node 回调向 async/await 转变
  • 反思总结然后整装待发
  • 记录一下第一次使用npm
  • 坑!为什么View.startAnimation不起作用?
  • 理解在java “”i=i++;”所发生的事情
  • 聊聊redis的数据结构的应用
  • 首页查询功能的一次实现过程
  • 微信小程序设置上一页数据
  • 线性表及其算法(java实现)
  • ​Benvista PhotoZoom Pro 9.0.4新功能介绍
  • ​水经微图Web1.5.0版即将上线
  • # 移动硬盘误操作制作为启动盘数据恢复问题
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • (k8s)Kubernetes本地存储接入
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (函数)颠倒字符串顺序(C语言)
  • (算法)前K大的和
  • (转)四层和七层负载均衡的区别
  • (转)原始图像数据和PDF中的图像数据
  • (转载)OpenStack Hacker养成指南
  • .net core 6 使用注解自动注入实例,无需构造注入 autowrite4net
  • .Net FrameWork总结
  • .net framwork4.6操作MySQL报错Character set ‘utf8mb3‘ is not supported 解决方法
  • .NET成年了,然后呢?
  • .NET实现之(自动更新)