JVM-类加载机制

news2024/10/7 10:16:54

类的生命周期

​ 其中类加载的过程包括了加载验证准备解析初始化五个阶段。在这五个阶段中,加载验证准备初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,**它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的运行时绑定(也成为动态绑定或晚期绑定)。**另外注意这里的几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。

类的加载(查找并加载类的二进制数据)

加载时类加载过程的第一个阶段,在加载阶段,虚拟机需要完成以下三件事情:

  • 通过一个类的全限定名来获取其定义的二进制字节流
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  • 在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口

JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误进行报告错误,类加载器必须在程序首次主动使用该类时才报告错误(LinkageError错误)如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误。

连接

验证

​ 验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证阶段大致会完成4个阶段的检验动作:

  • 文件格式验证: 验证字节流是否符合Class文件格式的规范;例如: 是否以0xCAFEBABE开头、主次版本号是否在当前虚拟机的处理范围之内、常量池中的常量是否有不被支持的类型。
  • 元数据验证: 对字节码描述的信息进行语义分析(注意: 对比javac编译阶段的语义分析),以保证其描述的信息符合Java语言规范的要求;例如: 这个类是否有父类,除了java.lang.Object之外。
  • 字节码验证: 通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
  • 符号引用验证: 确保解析动作能正确执行。

​ 验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用-Xverifynone参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

准备(为类的静态变量分配内存,并将其初始化为默认值)

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。对于该阶段有以下几点需要注意:

  • 这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中。
  • 这里所设置的初始值通常情况下是数据类型默认的零值(如00Lnullfalse等),而不是被在Java代码中被显式地赋予的值。

需要注意如下几点:

  • 对基本数据类型来说,对于类变量(static)和全局变量,如果不显式地对其赋值而直接使用,则系统会为其赋予默认的零值,而对于局部变量来说,在使用前必须显式地为其赋值,否则编译时不通过。
  • **对于同时被staticfinal修饰的常量,必须在声明的时候就为其显式地赋值,否则编译时不通过;**而只被final修饰的常量则既可以在声明时显式地为其赋值,也可以在类初始化时显式地为其赋值,总之,在使用前必须为其显式地赋值,系统不会为其赋予默认零值。
  • 对于引用数据类型reference来说,如数组引用、对象引用等,如果没有对其进行显式地赋值而直接使用,系统都会为其赋予默认的零值,即null
  • 如果在数组初始化时没有对数组中的各元素赋值,那么其中的元素将根据对应的数据类型而被赋予默认的零值。
  • **如果类字段的字段属性表中存在ConstantValue属性,即同时被final和static修饰,那么在准备阶段变量value就会被初始化为ConstValue属性所指定的值。**假设上面的类变量value被定义为: public static final int value = 3;编译时Javac将会为value生成ConstantValue属性,在准备阶段虚拟机就会根据ConstantValue的设置将value赋值为3。我们可以理解为static final常量在编译期就将其结果放入了调用它的类的常量池中

解析(类中的符号引用转换为直接引用)

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,解析动作主要针对接口字段类方法接口方法方法类型方法句柄调用点限定符7类符号引用进行。符号引用就是一组符号来描述目标,可以是任何字面量

直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标对象的句柄

初始化

初始化,为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。在Java中对类变量进行初始值设定有两种方式:

  • 声明类变量是指定初始值
  • 使用静态代码块为类变量指定初始值

JVM初始化步骤

  • 假如这个类还没有被加载和连接,则程序先加载并连接该类
  • 假如该类的直接父类还没有被初始化,则先初始化其直接父类
  • 假如类中有初始化语句,则系统依次执行这些初始化语句

类初始化时机: 只有当对类的主动使用的时候才会导致类的初始化,类的主动使用包括以下六种:

  • 创建类的实例,也就是new的方式
  • 访问某个类或接口的静态变量,或者对该静态变量赋值
  • 调用类的静态方法
  • 反射(如Class.forName(“com.pdai.jvm.Test”))
  • 初始化某个类的子类,则其父类也会被初始化
  • Java虚拟机启动时被标明为启动类的类(Java Test),直接使用java.exe命令来运行某个主类

使用

类访问方法区内的数据结构的接口, 对象是Heap区的数据。

卸载

Java虚拟机将结束生命周期的几种情况

  • 执行了System.exit()方法
  • 程序正常执行结束
  • 程序在执行过程中遇到了异常或错误而异常终止
  • 由于操作系统出现错误而导致Java虚拟机进程终止

类加载器

类加载器的层次

在这里插入图片描述

Java虚拟机的角度

​ 只存在两种不同的类加载器: 启动类加载器: 它使用C++实现(这里仅限于Hotspot,也就是JDK1.5之后默认的虚拟机,有很多其他的虚拟机是用Java语言实现的),是虚拟机自身的一部分;所有其他的类加载器: 这些类加载器都由Java语言实现,独立于虚拟机之外,并且全部继承自抽象类java.lang.ClassLoader,这些类加载器需要由启动类加载器加载到内存中之后才能去加载其他的类。

开发人员的角度

启动类加载器: Bootstrap ClassLoader,负责加载存放在JDK\jre\lib(JDK代表JDK的安装目录,下同)下,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.*开头的类均被Bootstrap ClassLoader加载)。启动类加载器是无法被Java程序直接引用的

扩展类加载器: Extension ClassLoader,该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载JDK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。

应用程序类加载器: Application ClassLoader,该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器

​ 应用程序都是由这三种类加载器互相配合进行加载的,如果有必要,我们还可以加入自定义的类加载器。因为JVM自带的ClassLoader只是懂得从本地文件系统加载标准的java class文件,因此如果编写了自己的ClassLoader,便可以做到如下几点:

  • 在执行非置信代码之前,自动验证数字签名。
  • 动态地创建符合用户特定需要的定制化构建类。
  • 从特定的场所取得java class,例如数据库中和网络中。

类加载方式

命令行启动应用时候由JVM初始化加载

通过Class.forName()方法动态加载

Class.forName()将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块

Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。并且只有调用了newInstance()方法采用调用构造函数,创建类的对象

通过ClassLoader.loadClass()方法动态加载

将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

类加载体系

一个类一旦被加载时,这个类引用的所有类也同时会被加载。类加载过程是一个递归的模式,所有相关的类都会被加载。但并不一定是一个应用里面所有类都会被加载,与这个被加载类的引用链无关的类是不会被加载的,直到有引用关系的时候它们才会被加载。

​ Java内置的类加载器在加载一个类之前会检查它是否已经被加载。因此重载一个类是无法使用Java内置的类加载器的,如果想要重载一个类你需要手动继承ClassLoader。

JVM类加载机制

类加载机制有四种:全盘负责、父类委托、缓冲机制、双亲委派机制。

全盘负责,当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。

父类委托,先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。

缓存机制,**缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。**这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效。

双亲委派机制, 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。向上查询。向下加载

双亲委派优势

  • 系统类防止内存中出现多份同样的字节码
  • 保证Java程序安全稳定运行

自定义类加载器

自定义类加载器一般都是继承自 ClassLoader 类。自定义类加载器的核心在于对字节码文件的获取,如果是加密的字节码则需要在该类中对文件进行解密

这里有几点需要注意 :

1、这里传递的文件名需要是类的全限定性名称,即com.pdai.jvm.classloader.Test2格式的,因为 defineClass 方法是按这种格式进行处理的。

2、最好不要重写loadClass方法,因为这样容易破坏双亲委托模式

3、这类Test 类本身可以被 AppClassLoader 类加载,因此我们不能把com/pdai/jvm/classloader/Test2.class 放在类路径下。否则,由于双亲委托机制的存在,会直接导致该类由 AppClassLoader 加载,而不会通过我们自定义类加载器来加载。

loadClass:JVM在加载类的时候,都是通过ClassLoader的loadClass()方法来加载class的,loadClass使用双亲委派模式。如果要改变双亲委派模式,可以修改loadClass来改变class的加载方式。双亲委派模式这里就不赘述了。
    findClassClassLoader通过findClass()方法来加载类。自定义类加载器实现这个方法来加载需要的类,比如指定路径下的文件,字节流等
    definedClass:definedClass在findClass中使用,通过调用传进去一个Class文件的字节数组,就可以方法区生成一个Class对象,也就是findClass实现了类加载的功能了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/509640.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ext-5 void GPIO1Pin23PinMuxSetup(void)含义

在阅读starterware里控制led的代码,GPIO初始化有个这个函数 void GPIO1Pin23PinMuxSetup(void) {HWREG(SOC_CONTROL_REGS CONTROL_CONF_GPMC_A(7)) CONTROL_CONF_MUXMODE(7); } 如果不熟悉可能看的一头雾水,这个含义是: 解释一下&#xff…

【Nginx】Nginx面试题

什么是Nginx? Nginx是一个 轻量级/高性能的反向代理Web服务器,用于 HTTP、HTTPS、SMTP、POP3 和 IMAP 协议。他实现非常高效的反向代理、负载平衡,他可以处理2-3万并发连接数,官方监测能支持5万并发,现在中国使用ngin…

U-boot常用命令(二)

FAT格式文件系统操作命令 有时候需要在uboot中对SD卡或EMMC中存储的文件进行操作,这时候就要用到文件操作命令。跟文件操作相关的命令有:fatinfo、fatls、fstype、fatload 和 fatwrite,但是这些文件操作命令只支持 FAT 格式的文件系统&#…

PCA(Principal Component Analysis,主成分分析)降维

1.PCA介绍 来源:视频https://www.bilibili.com/video/BV1E5411E71z/ 笔记:https://www.bilibili.com/read/cv23587690?spm_id_from333.999.0.0&jump_opus1 PCA就是找坐标系,使得数据在保留一维数据,损失是最小的。 目标&a…

Aescripts StyleX for AE(AI技术风格化插件)

Aescripts StyleX一款非常实用的视频风格和效果自动化处理插件,它提供了多种风格和效果、自动化处理、可自定义选项、支持多种分辨率、简单易用的界面和工具等功能,让用户可以轻松地为视频添加不同的风格和效果,并提高效率和准确性。StyleX 插…

基于STM32+RC522设计的门禁系统

一、项目背景 门禁系统是现代社会中非常重要的安全控制系统之一,其功能是在保障建筑物安全的同时,为合法用户提供便利。当前设计一种基于STM32+RC522的门禁系统设计方案,通过RFID-RC522模块实现了对用户卡的注册、识别及身份验证,通过控制SG90舵机实现门锁的开关,具有较高…

物联网架构和技术:如何实现物物互联和智能化控制

第一章:引言 物联网是一种新兴的技术领域,通过将物理设备、传感器和软件等连接起来,可以实现设备之间的互联互通,让各种设备可以进行数据交换和智能化控制。在这个数字化时代,物联网已经成为了连接万物的关键技术之一…

WiFi(Wireless Fidelity)基础(五)

目录 一、基本介绍(Introduction) 二、进化发展(Evolution) 三、PHY帧((PHY Frame ) 四、MAC帧(MAC Frame ) 五、协议(Protocol) 六、安全&#x…

程序设计入门——C语言 2023年5月9日

程序设计入门——C语言 一、基础理解1、什么是计算机?如何用计算机做事情?什么是应用软件?编程语言干啥的?计算机与人脑?学习编程能干啥?2、计算机-程序-算法3、解释语言vs编译语言 课程来源:链…

测试老鸟总结,性能测试常见瓶颈分析与调优,“我“也要卷出测试圈...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 性能测试过程中&a…

KingbaseES V8R3 集群运维系列 -- failover切换后集群自动恢复

​ 案例说明: KingbaseES V8R3集群默认在触发failover切换后,为保证数据安全,原主库需要通过人工介入后,恢复为新的备库加入到集群。在无人值守的现场环境,需要在触发failover切换后,主库可以自动恢复为新备…

jvm之图形化工具

写在前面 本文一起看下jvm图形化相关的工具,图形化的工具相比于命令行工具的好处是更加直观,也更容易分析问题。 1:jconsole 1.1:准备要连接的jvm进程 定义如下的类; package a.b; import java.util.concurrent.*…

Linux内核架构和工作原理

**前言:**作用是将应用层序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址。目前支持模块的动态装卸(裁剪)。Linux内核就是基于这个策略实现的。Linux进程1.采用层次结构,每个进程都依赖于一个父进程。内…

JavaScript模板引擎Template.js基本使用详解

template.js是一款JavaScript模板引擎,提供一套模板语法,简单好用,开发者可以写一个模板区块,每次传入的数据,生成对应数据产生的HTML片段,渲染不同的效果。官网:简洁语法版 https://github.com…

天猫数据分析:2023年Q1天猫净水器品牌销售TOP10排行榜

水质的好坏更是与人们的身体健康密切相关。随着社会经济的发展,居民生活水平提升,人们对饮用水质量、安全性的要求也不断提高,净水器也因此逐渐成为现代生活的必需品。 根据鲸参谋电商数据显示,2023年Q1在天猫平台上,净…

微服务#1 注册中心eareka和nacos

目录 注册中心: eureka 服务调用关系 eureka的作用 在Eureka架构中,微服务角色有两类 搭建EurekaServer服务, 服务注册和服务发现 Ribbon 负载均衡原理 ​编辑 负载均衡策略 饥饿加载 注册中心: nacos nacos服务搭建, 服务注册和发现 nacos服务集群属性 …

【shell脚本里的数组】

目录 一、数组的定义方法1、定义数组1.1、使用shell脚本来写出怎么查看这个数组有没有缺少元素1.2、使用shell脚本,来生成数组1.3、元素的切片 二、数组追加元素三、传输和传出1、向函数传入数组的值2、从函数返回数组 一、数组的定义方法 ( 20 10 60…

零基础入门人工智能学习路线:数学、编程、机器学习、深度学习和实践项目

如果你想开始学习人工智能,但是对于如何开始感到困惑,那么这里有一些零基础入门学习路线的建议,可以帮助你更好地理解和掌握人工智能的基础知识。 数学基础 作为人工智能的核心基础,数学知识是必不可少的。尤其是线性代数和概率论…

线性结构-链表

链表也是一种常用的线性数据结构,与数组不同的是,链表的存储空间并不连续,它是用一组地址任意的存储单元来存放数据的,也就是将存储单元分散在内存的各个地址上。 这些地址分散的存储单元叫做链表的节点,链表就是由一个…

亲测好用|甲方、专家和领导,用三维模型汇报方案如何投其所好?

身为设计方的你,有没有这样的经历: ➤ 一个非常优秀的方案未能被甲方采纳,反而甲方选择了一个不如自己的方案,造成了很大的遗憾; ➤ 在讲述自己的设计方案的时候,经常越说越散,甚至到了最后自…