JVM 类的加载篇

news2024/11/20 9:47:38

我们都知道一个类从加载到卸载一共分为七个过程

加载 - 链接(验证 - 准备 - 解析) - 初始化 - 使用 - 卸载

下文我们将详细解析这些过程

谁需要加载?

在Java中数据类型分为基本数据类型和引用数据类型,基本数据类型由虚拟机预定义,引用数据类型则需要类的加载

1.加载/装载(loading)

装载的过程就是将Java字节码加载到机器内存中,并在内存中构建出Java类的模板class对象

简而言之,就是将二进制的数据加载进内存变成class实例

大概可以概括为三个问题

1.通过类的全名获取二进制数据流

2.解析二进制数据流为方法区数据结构/类模型

3.创建java.lang.Class实例,表示该模型,作为这个类数据的访问入口

什么是类模板?

所谓类模板,其实就是Java类在JVM内存中的一个快照,JVM在字节码文件中解析出的常量池,类字段,类方法等信息存储到一个类模板中,这样JVM在运行的时候就可以在模板中获取类的任何信息,可以进行任何操作

反射就基于这一基础,如果没有类模板,JVM在运行时也无法进行反射操作

注:类模型存在方法区中

二进制流有哪些获取方式?

对于类的二进制文件,虚拟机有很多种获取方式

1.class文件

2.jar包,zip包等

3.数据库中的类的二进制文件

4.通过网络HTTP协议进行加载

在虚拟机获取到类的二进制信息之后就会加载一个Class实例,但是如果二进制文件不满足要求,则会抛出异常

Class文件在哪?

.class文件加载到元空间之后就会在堆区创建一个Class对象,用来封装类的数据结构,这个class对象是加载类的过程中创建的,每个类都对应有class对象

这样外界就可以直接通过访问CLass对象来获取class的数据结构了

特殊:数组类的加载

数组类本身不是由类的加载器负责创建而是JVM运行的时候根据徐区域直接创建的,但是数组元素类型仍然需要依赖类的加载器去创建 ,如果这里数组的元素是引用类型,那么就需要正常递归加载元素类型

2.链接(linking)

1.验证 

这里的需求就是验证加载进虚拟机的class文件是否符合java虚拟机规范,大部分要做如下检查

注:其中格式验证会在装载阶段一并执行,验证通过后才会将二进制数据加载到方法区(元空间)

说明:

1.格式验证  看开头魔数是不是0xCAFEBABE版本号是否支持等

2.语义检查,比如是否final修饰的方法或者类被重写或者继承了

3.字节码验证 比如看函数的调用是否指向了正确的类型参数,变量的赋值是否是正确的类型等

注:通过字节码验证也不能说明这个类完全没问题

4.符号引用验证         看符号引用是都能在常量池中找到对应的直接引用

2.准备阶段

这个阶段会为类的静态变量进行分配内存,这个阶段虚拟机会为类进行分配空间,并设置默认的初始值,比如int 赋值为0  long类型赋值为0L一样

注:1.Java不支持boolean类型,对于boolean类型,内部实现是int,int的初始化值为0,对应的boolean默认值就是false

2.修饰为static final的,在编译的时候就会分配,准备阶段会显示赋值

3.这里不会为实例变量进行初始化,实例变量会随着对象一起分配到堆中

4.这个阶段不会有代码被执行

3.解析阶段

简而言之就是将类,方法,接口,字段的符号引用转化为直接引用

Java虚拟机为每个类都准备了一个方法表,所有的方法都列在表里,当需要调用一个类的方法的时候,通过解析将符号引用转化为直接引用就可以得到目标方法在方法表的位置,从而调用方法.

注:解析就是将符号引用转化为直接引用,也就是得到类,字段等在内存中的引用或者是偏移量,可以说如果直接引用存在,系统中一定存在这样的类方法或者字段,符号引用则不能确定,但是虚拟机规范没有规定一定要按照顺序执行,HotSpot中解析就是在初始化之后再执行的

初始化(initiating)

这里的初始化其实就是对静态变量的值进行显示初始化了

类的初始化是类装载的最后一个阶段,到了这里才开始真正意义上执行了Java程序代码

重要方法:<clinit>()    有静态变量才有的方法

这个方法是Java编译器生成并且由JVM调用的,我们无法自定义重名方法,也不能调用这个方法

这个方法主要就是给类的static变量显示赋值和在静态代码块中赋值了

<init>方法,一定会出现在class表中的method中,涉及显示赋值,代码块和构造方法

先加载父类再加载子类?

加载一个类的时候虚拟机总是先加载他的父类,因为父类的clinit总在子类之前被调用,这也就说明了为什么父类的静态代码块要优先于子类了

哪些类没有clinit方法?

1.没有类变量和静态代码块的类

2.一个类中声明类变量但是没有使用初始化语句和静态代码块赋值操作

3.一个类中包含static final 修饰的基本数据类型的字段,这些类字段初始化语句采用编译时常量表达式

clinit会产生死锁嘛?

类的初始化调用这个方法的时候虚拟机会保证其在多线程环境下正确的被加载同步,如果多个线程去初始化同一个类,只会有一个线程去执行这个clinit方法,正是因为这个方法是线程安全的,所以如果在一个类的clinit方法中有很耗时的操作,那么就可能造成多个线程阻塞的操作,从而引发死锁

主动使用 VS 被动使用

主动使用:Class只有在首次使用的时候才会被装载,java虚拟机不会无条件的装载Class类,Java虚拟机规定,一个类或者是接口在初次使用的时候一定要初始化,这里的使用就是主动使用

主动使用(默认这里加载,验证,准备已经完成)

1.创造类的实例的时候(包括反射,克隆,反序列化)

2.调用类的静态方法的时候

3.使用接口类的静态字段的时候

4.初始化子类发现父类没初始化,就先触发父类的初始化

5.虚拟机启动时,主类main()需要初始化

等等

被动使用:不会引起类的初始化

1.访问静态字段时,只有真正声明这个字段的类会初始化

2.使用数组定义类应用不会出现初始化

3.loadClass()方法记载一个类,也不会导致初始化

被动使用不会进行clinit方法的调用

类的使用

一旦这个类经过了装载,验证,准备,解析,初始化五个阶段,这时候就可以给开发者使用了,开发人员可以在程序中调用它的静态类成员信息,或者使用new关键字创建对象实例了.

类的卸载

我们先展示一个图

这个图表示了一个对象被创建出来之后,默认指向它的引用,这里其他的引用都好去除,就是类的加载器这个引用无法去除,如果是三个系统自带的类加载器,那么无法删除这个引用,这也就说明了为啥JDK8要将永久代变成元空间使用本地内存了,因为类几乎是无法卸载的,除非使用自定义的类的加载器,这才有可能将这个引用删除,从而解决类的卸载问题

类的加载器

类的加载器是JVM执行类加载机制的前提

ClassLoader作为Java的核心组件,所有的Class都是由ClassLoader进行加载的,它负责将class的二进制文件读进JVM内部,然后转化为Class实例,这里的classloader主要是在装载的阶段起作用

显示加载: 指的是在代码中调用ClassLoader来加载class对象如直接写Class.forName(name)

隐式加载:通过JVM自动进行加载,只要class文件中引用到了另一个类的对象,就会自动加载到内存中

加载的类是唯一的吗?

什么叫唯一:由两方面决定,加载器和类本身 比较两个类是否相等,只有这两个都相同才是相等的,不然即使是同一份class文件,被同一个虚拟机加载,只要加载的类加载器不同,那么这个两个类也一定不同    所以加载的类不是唯一的

双亲委派模型

定义:如果一个类加载器在接收到加载类的请求的时候,首先不会自己尝试加载这个类,而是把这个请求任务委托给父类加载器去完成,依此递归,如果父类加载器能完成就完成,完成不了再由子类加载器来完成

本质:规定了加载顺序 引导类先加载,拓展类其次,系统类最后,再是自定义类加载器

我们再说说三个JVM自带的加载器加载哪些内容吧

引导类加载器:加载JVM需要的类

扩展类加载器:记载标准扩展的类

系统类加载器:加载path下指定的类以及上放没有包含的类

源码分析

双亲委派机制在java.lang.ClassLoader.loadClass(String boolean)接口中体现,逻辑如下

1.在加载器的缓存查找有无目标类,有就直接返回

2.看父加载器是否为空,不为空则调用父加载器的接口进行加载

3.如果父加载器为空,则调用引导类加载器进行加载

4.以上都无法加载,就调用Classloader接口中的defineClass系列的native接口加载目标Java类

注:这里不要去想重写loadclass方法来打破双亲委派机制,因为不管是什么类加载其最后都会执行predeDineClass接口,这就是堆核心JDK库的保护

双亲委派机制的优势和劣势

双亲委派机制优势

1.避免了类的重复加载

2.保护核心api的安全

缺点

检查类是否加载的委托过程是单向的,这个方式虽然架构清晰,职责明确,但是顶层的加载器就不能访问底层的加载器所加载的类了,通常情况下,启动类加载器的类称为系统核心类,包括重要的系统接口,应用类访问启动类加载器加载的类自然没问题,但是启动类加载器访问不了应用类加载器加载的类,比如系统类加载器提供一个接口在应用类得以实现,该接口绑定一个工厂方法,用于创建实例,而接口和工厂方法都在启动类加载器中,此时就会出现工厂方法无法创建启动类加载器加载的应用实例的问题

破坏双亲委派机制的举例

1.由于类加载器是在jdk1.0的时候引入的,而jdk1.2才引入双亲委派模型,设计者不得不做出妥协,为了兼容这些代码,无法再以技术手段避免loadClass()被子类覆盖的可能性,只能在lang.ClassLoader中增加一个新的方法findCLass,引导用户尽可能去重写这个方法,按照loadClass的逻辑,在父类加载失败的时候就会来使用这个findCLass完成加载

2.线程上下文加载器

这是因为模型本身的缺陷导致的

如果有基础类型要回调用户的代码怎么办呢

典型的就是JNDI服务,在JDK1.3引入,目的是堆资源进行查找和集中管理,但是他是由启动类加载器进行加载的,需要调用其他厂商实现的SPI(服务提供者接口),通常把核心类提供外部服务并可以由应用层实现的接口称之为SPI

这里线程上下文加载器就要出手了,这个类加载器可以通过Thread类setContextClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器,这时候问题也就迎刃而解了.

3.代码热部署/模块热替换

这就是由用户追求程序动态性导致的

IBM公司主导的视线模块而部署的关键是自定义的类加载机制的实现,每个程序都实现一个自己的类加载器,需要更换的时候直接将加载器一起替换,这里就不遵循双亲委派机制的树状结构,而是进一步发展成网状结构

'

热替换:这里就是不停止服务,止痛膏替换程序文件来修改程序的行为,关键在于服务不能中断,修改也必须立即表现扎起正在运行的系统上

市面上大部分脚本语言是支持热替换的,但是java不是天生支持的,所以只能使用ClassLoader了

注:不同ClassLoader即使加载两个相同的类,也是会认为是不同的类型的,所以可以实现,基本思路如下图

TomCat类加载机制

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

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

相关文章

腾讯云最新优惠券领取入口及使用指南

​腾讯云作为国内领先的云服务提供商&#xff0c;以其稳定、高效的服务赢得了广大用户的信赖。为了吸引用户&#xff0c;腾讯云经常会推出各种优惠活动&#xff0c;其中最常见的就是腾讯云优惠券。本文将为大家分享腾讯云最新优惠券领取入口及使用指南&#xff0c;助力大家轻松…

从汇编来角度剖析C语言函数调用过程

目录 1.引言 2.寄存器 3.栈帧 4.函数调用前调用者的动作 5.被调用者在函数调用后的动作 6.被调用者返回前的动作 7.调用者在返回后的动作 8.总结 1.引言 当一个c函数被调用时&#xff0c;一个栈帧(stack frame)是如何被建立&#xff0c;又如何被消除的。这些细节跟操作…

爆肝整理万能sass框架:react18+webpack5+typescript+ant Design,框架在手,交付无忧!!!

来活了&#xff0c;要求一周时间内快速给xxx业务开发一个sass系统平台&#xff0c;要求有角色权限控制&#xff0c;推荐模块&#xff0c;各种业务内容模块&#xff0c;莫慌&#xff0c;直接上代码&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 1.系统框架配置…

前端跨页面通信的几种方式---同源

参考链接 1、LocalStorage:当 LocalStorage 变化时&#xff0c;会触发storage事件。利用这个特性&#xff0c;我们可以在发送消息时&#xff0c;把消息写入到某个 LocalStorage 中&#xff1b;然后在各个页面内&#xff0c;通过监听storage事件即可收到通知。 2、BroadCast C…

12 list的使用

文档介绍 文档介绍 1.list是可以在常数范围内的任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代 2.list的底层是带头双向链表循环结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前一个元素和…

C++的This详解

目录 听说点赞的UU会有好运哦&#xff01; 听说点赞的UU会有好运哦&#xff01; 听说点赞的UU会有好运哦&#xff01; this指针&#xff1a; 面试常考this&#xff1a; 特别注意&#xff1a; this指针&#xff1a; 类不保存成员函数&#xff0c;而是放在公共代码段&…

Flutter第四弹:Flutter图形渲染性能

目标&#xff1a; 1&#xff09;Flutter图形渲染性能能够媲美原生&#xff1f; 2&#xff09;Flutter性能优于React Native? 一、Flutter图形渲染原理 1.1 Flutter图形渲染原理 Flutter直接调用Skia。 Flutter不使用WebView&#xff0c;也不使用操作系统的原生控件,而是…

【计算机网络实践】FileZilla Server1.8.1实现局域网ftp文件传输

大二新生随便写写笔记&#xff0c;轻喷&#xff0c;鉴于本人在网络搜索中并未搜索到1.8.1版本的使用方法&#xff0c;因而瞎写一页。 一、准备 下载一个FileZilla Server1.8.1在你想作为服务器的主机上&#xff08;此处直接在官网下载即可&#xff1a;Download FileZilla Serve…

stimulsoft report for js vue3使用

项目后端使用的java&#xff0c;试验过积木报表&#xff08;web界面类型的&#xff09;、JasperReport&#xff08;.jasper报表文件&#xff09;、stimulsoft web版本&#xff08;.mrt报表文件&#xff09; 我们的项目是前后端分离的&#xff0c;用积木报表&#xff08;开箱即…

Spring boot 集成netty实现websocket通信

一、netty介绍 Netty 是一个基于NIO的客户、服务器端的编程框架&#xff0c;使用Netty 可以确保你快速和简单的开发出一个网络应用&#xff0c;例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程&#xff0c;例如&#xff1a;基于TCP和U…

【研发日记】,Matlab/Simulink开箱报告(十)——Requirements Toolbox

前言 见《开箱报告&#xff0c;Simulink Toolbox库模块使用指南&#xff08;五&#xff09;——S-Fuction模块(C MEX S-Function)》 见《开箱报告&#xff0c;Simulink Toolbox库模块使用指南&#xff08;六&#xff09;——S-Fuction模块&#xff08;TLC&#xff09;》 见《开…

狂揽Github—start19.7k☆开源OCR—Umi-OCR

文章目录 背景Umi-OCR—源码下载Umi-OCR—可执行程序下载页面介绍截图OCR识别批量OCR识别批量文档二维码全局设置 总结&#xff1a; 背景 大家都知道我是一个Python办公自动化的小小程序员&#xff0c;经常收集一些免费开源的OCR供大家使用&#xff0c;目前我已经写出来多家OCR…

集智书童 | 炸裂 !轻量化YOLO | ShuffleNetv2与Transformer结合,重塑YOLOv7成就超轻超快YOLO

本文来源公众号“集智书童”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;炸裂 &#xff01;轻量化YOLO | ShuffleNetv2与Transformer结合&#xff0c;重塑YOLOv7成就超轻超快YOLO 随着移动计算技术的迅速发展&#xff0c;在移动…

flutter入门

本文真对 Flutter 的技术特性&#xff0c;做了一些略全面的入门级的介绍&#xff0c;如果你听说过Flutter&#xff0c;想去了解它&#xff0c;但是又不想去翻厚厚的API&#xff0c;那么本文就是为你准备的。 随着纯客户端到Hybrid技术&#xff0c;到RN&Weex&#xff0c;再…

【OpenGL手册13】 光照贴图

目录 一、说明二、漫反射贴图三、镜面光贴图四、采样镜面光贴图练习 一、说明 在上一节中&#xff0c;我们讨论了让每个物体都拥有自己独特的材质从而对光照做出不同的反应的方法。这样子能够很容易在一个光照的场景中给每个物体一个独特的外观&#xff0c;但是这仍不能对一个…

C#重新认识笔记_ FixUpdate + Update

C#重新认识笔记_ FixUpdate Update Update: 刷新频率不一致,非物理对象的移动&#xff0c;简单的刷新可用&#xff0c; FixedUpdate: 刷新频率一致,按照固定频率刷新&#xff0c;一般调用FixedUpdate之后&#xff0c;会立即进入必要的物理计算中,因此&#xff0c;任何影响刚…

层次式架构设计

本博客地址&#xff1a;https://security.blog.csdn.net/article/details/136660435 一. 概述 1、软件体系结构为软件系统提供了结构、行为和属性的高级抽象&#xff0c;由构成系统的元素描述这些元素的相互作用、指导元素集成的模式以及这些模式的约束组成。层次式体系结构设…

王庆:当下股市过于悲观,A股、港股基本完成补跌和普跌过程,逆向布局时机已到

核心观点&#xff1a; 1、房地产对中国经济增长拖累最严重的时期正在过去...密切关注真正拐点的出现。 2、当前资本市场从价格表现上来讲&#xff0c;表现的远远超过了基本面所决定的悲观程度。 由于当前资本市场过于悲观&#xff0c;那么反过来就是孕育着机会。 3、我们判…

leetcode刷题(javaScript)——分治思想(二分查找、快速排序)相关场景题总结

分治思想是一种将问题分解成更小的子问题&#xff0c;然后解决子问题并将结果合并的算法设计策略。二分查找、快速排序和折半查找都属于分治思想的经典算法。在leetcode里&#xff0c;分治思想一般结合其他场景出现&#xff0c;构成复合型题目。但是在看题时一定要了解能否用分…

Docker入门二(应用部署、迁移与备份)

文章目录 一、应用部署1.MySQL部署2.Redis部署3.Nginx部署 二、迁移与备份1.容器做成镜像2.把镜像被分成压缩包 一、应用部署 1.MySQL部署 在dokcer中部署mysql&#xff0c;以后不需要在宿主机上装mysql1.做端口映射docker run -id --namemysql5.7 -p 3306:3306 -e MYSQL_ROOT…