JVM--类加载机制全面大解析

类加载过程详解 类加载的过程 阅读详情

在说类加载之前,我先说一说类的文件结构,我们知道当编译Java代码的时候会生成.class文件,这种字节码方式保证了Java的可移植性,同时字节码又存储了整个类的信息,用字节码存储,所以字节码中每个字节都有它的意义,所以了解类文件的结构还是比较重要的

Class文件结构详解

它的文件结构就是根据ClassFile来构建的,接下来我们根据下面这个结构体一个一个属性来解析,来了解一下类文件结构

cpp

复制代码

ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count]; }

我们也可以通过一些插件来看这个信息,比如idea中的jclasslib插件,如下图

magic魔法数

每一个class文件的前四个字节称之为魔法数,它是一个标识,固定值是0xCAFEBABE,作用就是确定class文件能否虚拟机接收,所以这是Java的一个规范,如果魔法数不匹配,虚拟机将拒绝加载它

minor_version和major_version,class的次版本号和主版本号

紧跟着魔法数四个字节的就是这两个属性,第5和6个字节存储次版本号,第7和8个字节存储主版本号。表示的就是编译Java文件的Java版本,这个是由官方决定的,而版本号的作用是什么呢?每当Java进行升级的时候,可能虚拟机也会升级,也就是完成版本对应,高版本的虚拟机可以兼容低版本的Class文件,但是低版本的虚拟机可能无法加载高版本的Class文件了,作用就是用于比对版本。

constant_pool_count常量池中常量数目,constant_pool[constant_pool_count-1]常量池数据

我们发现个小东西,常量池的数组的长度是数目-1,这是没问题的,数目是从1开始计算的,而所以是从0开始计算的,所以constant_pool[constant_pool_count-1]就代表着最后一个常量的值,如果计算的索引=0,那么就说明没有任何常量引用。

constant_pool_count是class文件的常量,也就是final修饰的,常量包括两种类型,一种是字面量,一种是符号引用。字面量就是我们常见的final int a =14;或者字符串字面量,final String s = "str";而符号引用属于编译原理方面的知识了它包括了类和接口的全限定名,字段的名称和描述符,方法的名称和描述符,如下图

常量池中每一项常量都一个表,一共有14种类型,每个表的开始的第一位是一个标识来标识是哪种类型,比如整型字面量,长整形字面量等等。

access_flags访问标志

这个标志来表示Class是类还是接口,public还是private等等,和Class的访问有关系的标志。

this_class当前类,super_class父类,interfaces_count实现的接口数,interfaces[interfaces_count];存储实现接口的索引地址

Java是单继承的机制,所以没有数组存储,同时如果一个类没有继承任何类,那么它的父类就是Object,所以父类的索引永远都不会为0,然后接口数组里面就是按照编写的顺序进行存储的,最后都是要根据值去找到对应的全限定类名。那去哪里找呢?就是上面的那个常量池中!

fields_count字段数量和fields[fields_count]字段

这个就是存储类中字段的信息,比如字段的作用域,名字和信息的索引(常量池中存储数据)等等。

methods_count方法数量和methods[methods_count]方法集合

和字段类似都是存储类中方法的信息

attributes_count属性数和attributes[attributes_count];属性集合

这个和字段的区别是,字段是存储字段的信息,比如作用于,名字之类的,属性代表的是字段的能力,比如get方法,set方法等等。

类的加载过程是啥样的?

在知道我们类的结构之后,那么我们开始说说类加载的过程。不多说,先上个图。

类的生命周期也就是上面的七步:加载->验证->准备->解析->初始化->使用->销毁,接下来我一个一个说。而我要说的是加载过程,也就是五步:加载->验证->准备->解析->初始化

类加载过程---加载

记载这一步主要是由类加载器完成的,而使用哪种类加载器是由双亲委派模型决定的,这个部分我会在下面的类加载器模块中详细讲解。每一个类都有一个引用指向加载它的ClassLoader,数组是个例外,而是在JVM需要的时候通过getClassLoader根据数组元素的类型来获取的。

在加载阶段虚拟机根据全类名获取类的内容,也就是我们上面说的ClassFile,然后将二进制的内容所代表的静态结构转换为方法区(到时候说内存模型的时候讲)的运行时数据结构,然后在内存中生成一个代表该类的对象实例。

当然进行加载的过程也会完成一部分验证的步骤,比如字节码验证,从静态结构转为运行时数据结构的时候就是对字节码的解析,所以要同时验证字节码

类加载过程---验证

验证是链接阶段的第一步,这一步是非常重要的,就是确保Class文件的字节流包含的信息符合《Java虚拟机规范》的全部约束。主要进行四个步骤的校验。

1. 文件格式验证: 比如我们上面说的魔法书验证,主次版本号验证,常量池中的常量类型有没有超出范围等。

2. 元数据验证: 主要对字节码秒描述的信息进行分析,看是否符合规范,比如是否继承了不能继承的类等。

3. 字节码验证: 主要通过数据流和控制流分析判定语义是否合法,类型转换合不合理,参数类型对不对之类 的。

4. 符号引用验证: 验证类的正确性,比如引用其他类的方法是否存在,有没有权限访问等。

当然了这四个验证步骤不是串行的,而是分布在整个链接阶段的,比如符号引用验证就是在解析的阶段验证的,比如你访问了一个类的私有属性这种。所以验证阶段就是验证类的合法性和正确性。

类加载过程---准备

准备阶段就是为类变量分配内存和设置初始值的阶段,这些内存将在方法区中分配。而类变量指的是在类中用static修饰的变量,而且设置初始值不是开发人员指定的值,比如int类型的默认值是0,float类型的默认值为0.0f等。而且是在堆中分配的(1.8中的变化,讲内存模型的时候会介绍)。

类加载过程---解析

解析就是虚拟机将常量池中的符号引用替换为直接引用的过程,主要针对类或接口,字段,类方法,接口方法,方法类型,方法句柄(直接调用目标方法的一种底层表示,不同于反射,更加的高效),调用限定符(和方法句柄组合使用,实现更灵活的的分派机制)。怎么理解这个符号引用和直接引用呢。我在上面写了在ClassFile的常量池中存储了字面量和符号引用,这时候就体现出来了。我们调用一个方法的时候需要知道这个方法所在的位置,所以在上面的ClassFile中我们可以看到methods那个属性,而这个属性存储了方法在内存,也就是方法区的指针或者方法句柄,然后转换,这个过程就是直接引用。在准备阶段的时候就是将ClassFile的静态存储结构转换为方法区的动态结构,这个时候就已经在内存中了。所以符号引用很关键,在静态文件中标志着和其他类的各种关系,用它来转换成可执行的动态结构!所以转换过程就是将ClassFile中的常量池中的符号引用全都转换为直接引用,这就是解析阶段干的事。

类加载过程---初始化

这也是假加载类的最后一个阶段,完成对静态变量,实例变量的赋值,调用类的静态方法,new对象实例等,如果一个类的父类没有初始化,那么会先初始化父类。

到这里类的初始化就已经完成了,类的属性的赋值,对于方法的引用全都完成了,之后就是调用的事了,接下来说一说类加载这里的最后一块,类加载器。

类加载器

上面我在类加载阶段的加载中说到了,会使用ClassLoader去生成类对象,其实也就是生成一个Class对象放在方法区中,它也是所有这个类对象的一个参照。ClassLoader就是干这个的,它的主要工作就是将Java的类字节码加载到JVM中,在内存中生成一个代表该类的Class的对象。

类加载器的加载规则

类加载不是一次性全部加载的,而是动态的加载,大部分的类只有在使用的时候才会去加载,比如main方法所在的类和一些Java核心的库的类都是要加载的,这样对内存和性能更加的友好。在ClassLoader类中有这样一个属性。

java

复制代码

private final Vector<Class> classes = new Vector<>();

里面存储的就是该ClassLoader加载过的类,如果加载过了就不再加载了,而判定条件就是类的全限定类名+加载它的类加载器是不是一样的。 JVM中内置了三种类加载器,也是三层类加载器,分别是:

  1. BootStrapClassLoader: 启动类加载器,C++实现,没有父类,主要用于加载Java的核心类库,比如rt.jar,resources.jar等包含了我们常见的不如java.lang,java.util,java.io。等等
  2. ExtensionClassLoader: 扩展类加载器,主要加载lib/ext下的jar包和类。
  3. AppClassLoader: 应用程序类加载器,主要面向我们的应用程序,加载classPath下所有的jar和类。

除了这三个之外,用户也可以自己定义类加载器,用于实现独特的功能,比如想给字节码加密这种。如果自己实现ClassLoader,那么就继承ClassLoader类,然后去实现它的方法,它的方法有两个需要我们注意的,一个是findClass,一个是loadClass,这是有区别的。这里有一个概念叫双亲委派模型,这个模式是用于确定我们加载的类是由哪种类加载器加载的,如果重写了findClass那么就不打破双亲委派模型,如果重写了loadClass那么就打破双亲委派模型。说了半天双亲委派模型是啥东西?接着说。

双亲委派模型

直接上图,更加清晰。

实际上就是当加载类的时候先看自己这个类的ClassLoader是不是加载了本类,如果没有在去类加载器的父级类加载器中找加没加载过,一直到最顶层的BootStrapClassLoader,如果都没有,就尝试用最顶层的类加载器去加载,如果加载不了,那么就往下委托去加载。简而言之就是,自下而上查找,自上而下加载。

双亲委派模型的好处

既然我们明白了双亲委派模型的过程,那么我们说说它的好处。第一个好处就是避免了类的重复加载,第二个好处就是避免了Java的核心类库被覆盖。第二个很重要,如果我们没有双亲委派模型的机制,那么我们甚至可以更改java.Object下的类,这很危险。而我们引用第三方包的时候有时候会修改它的源码,所以通常做法就是在classpath下创建一个相同的全限定类名的类,第三方包也属于classpath下,属于AppClassLoader加载,就能完成覆盖的效果了。

Tomcat打破双亲委派模型

我上面也说了,打破双亲委派模型的方式,而Tomcat就需要打破,这是有原因的。在Tomcat中可能需要部署多个web应用,也就是我们常见的webapp,而不同的web应用可能存在相同名字的类,但是功能不同,版本不同,但是还是需要加载的。所以在Tomcat中每一个webApp都有它自己的一个WebappClassLoader,web应用自己的类要首先由自己的WebappClassLoader加载,否则交给ExtensionClassloader加载,这就打破了双亲委派模型了。

SPI中打破双亲委派模型

SPI的接口比如java.sql.Driver是由Java的核心库提供的,需要BootStrapClassLoader加载,而它的实现是由第三方包提供的,比如com.mysql.cj.jdbc.Driver,这个需要应用程序类加载器或者自定义加载器加载。但是吧,BootStrapClassLoader是没办法找到SPI的实现类的,也就没法委托子类加载器去尝试加载。所以为了解决这个问题,Java的设计团队设计了一个线程上下文类加载器,通过java.lang.Thread中的getContextClassLoader或者setContextClassLoader进行获取和设置,比如JNDI在使用这个来获取类加载器,以此来达到父加载器请求子加载器完成加载的动作了,但是这明显的违背了双亲委派模型,但也没啥办法。

那破坏双亲委派模型之后,能不能重写String类?

即便是破坏了双亲委派模型,你重写了loadClass方法,但是还是需要调用父类的defineClass来将字节流转为JVM可是别的class,而defineClass其中有个判断就是全限定类名不能以java.开头,所以就控制了这个。

YIUI框架深度解析:基于ET的数据驱动UI开发实践 在Unity游戏开发中,UI管理是构建复杂应用的核心挑战之一。传统命令式编程需要开发者手动操作每个UI组件,导致业务逻辑与视图层高度耦合,代码难以维护。数据驱动是一种先进的架构思想,它通过建立数据模型与UI组件之间的绑定关系,实现数据变化时UI的自动更新,从而将开发者的关注点从“如何操作UI”转移到“数据状态是什么”。这种模式显著提升了代码的可维护性和开发效率,尤其适用于状态频繁变化的游戏界面。YIUI框架正是这一思想在Unity UGUI上的工程化实践,它通过Model-View-Component的清晰 阅读详情

相关推荐

openEuler 欧拉 安装Oracle19c数据库RPM包安装

欧拉系统安装部署oracle 19c rpm方式

陈苏漾的IT生涯 6891

类加载器 超详解:什么是类加载器,类加载器作用及应用场景,类加载时机,类加载的完整过程,类加载器分类

类加载器 超详解:什么是类加载器,类加载器作用及应用场景,类加载时机,类加载的完整过程,类加载器分类

qq_44700578的博客 7074

使用 Ansys Q3D 进行电容提取

精确的电容提取在高速和 RF 设计中至关重要。在本博客中,我们演示了如何使用Ansys Q3D Extractor来计算电容值,从基本的平行板电容器开始,然后转到MEMS芯片的切口,以检查焊盘和RF线对整体电容的影响。在下面的比较中,我们使用 Q3D 从 MEMS 芯片切口中提取两种配置的电容矩阵:一种仅带有焊盘,另一种带有连接到 RF 线路的焊盘。这种比较表明,焊盘是总电容的主要贡献者,而 RF 线路则略有增加。按照上述工作流程,提取焊盘的电容,得到的电容矩阵如下所示。,选择一个已识别的网络,然后单击。

jkh920184196的博客 1810

JVM 类加载机制详解

通过前面的分析,我们可以看出,除了和本地实现密切相关的启动类加载器之外,包括标准扩展类加载器和系统类加载器在内的所有其他类加载器我们都可以当做自定义类加载器来对待,唯一区别是是否被虚拟机默认使用。首先检查请求的类型是否已经被这个类装载器装载到命名空间中了,如果已经装载,直接返回;否则转入步骤2;委派类加载请求给父类加载器(更准确的说应该是双亲类加载器,真实虚拟机中各种类加载器最终会呈现树状结构),如果父类加载器能够完成,则返回父类加载器加载的Class实例;否则转入步骤3;

十年以上架构设计经验,专注于软件架构和人工智能领域,对机器视觉、NLP、音视频等领域都有涉猎 4780

World Action Model:经典论文

郑困时,图prorpt为机量人压务时,退过驱制解弱词表(constrsinsd decoding), 强附模型只从.256个者动作token中保H, B象总注活地。始VLA之一,RT-2使用了大量的五联网VQ据与少里的机械网的AcSonDat行CoFine-Tunng, 独组sl, 共i744个episode,Mous使用三个专累模,分是vidogonmedel,actonesget,understandng。包的核定之的的观到沟血黄动作,预之后现(vorlemodel),tseing,2。

u013250861的博客 406

AI驱动的iOS逆向工程自动化:14阶段工作流与Ghidra集成实践

逆向工程是软件安全分析的核心技术,通过反汇编、调试等手段解析程序内部逻辑与数据流。其原理在于将编译后的二进制代码还原为可理解的逻辑结构,从而发现潜在漏洞或理解软件行为。在移动安全领域,iOS逆向工程具有重要技术价值,能够用于安全审计、漏洞挖掘与恶意软件分析。传统iOS逆向依赖ipsw、class-dump、Frida等工具链组合,存在学习曲线陡峭与流程碎片化问题。本文介绍的ios-reverse-skills项目创新性地将14阶段逆向工程方法论封装为AI可调用的标准化脚本集,通过Ghidra脚本实现Swif

weixin_27268733的博客 430

一、类加载机制深度解析

第一步:java调用底层的jvm.dll文件创建java虚拟机(这一步由C++实现),这里java.exe是c++写的代码,调用jvm.dll也是c++底层的一个函数,通过调用jvm.dll文件(dll文件相当于java的jar包),会创建java虚拟机,java虚拟机的启动都是c++程序实现的。 第二步:在启动虚拟机的过程中,会创建一个引导类加载器的实例,这个引导类的加载器是C语言实现的,然后jvm虚拟机就启动起来了。 第三步:接下类,c++会调用java的启动程序,刚刚只是创建了java虚拟机,...

weixin_40181736的博客 1141

AD学习笔记(一)基础认识

文章目录AD学习笔记第一讲 AD基础认识一、两库两图(分别对应下图)1、PCB图(pcbdoc)2、原理图(schdoc)3、原理图库(schlib)4、PCB库(pcblib)二、封装1、DIP封装2、LQFP封装三、绘制整体流程1、建立工程(PrjPcb)2、建立两库两图并保存整个工程(五次)3、从库中提取绘制原理图4、原理图导入pcb图5、根据原理图布线6、pcb图交由工厂打板 AD学习笔记 第一讲 AD基础认识 一、两库两图(分别对应下图) PCB图(pcbdoc) 原理图(schdoc) PCB库

qq_52465473的博客 6686

类的加载过程详解

类的加载过程详解

永无止境 1023

最新测算源码PHP算命源码-风水八字-八字起名-塔罗牌占卜

未来,随着技术的发展和用户需求的变化,我们可以继续对系统进行升级和优化,以满足更多用户的需求和期望。数据库设计:创建一个名为bazi的数据库表,包含以下字段:id(自增ID)、name(姓名)、sex(性别)、birth_date(出生日期)、birth_time(出生时间)、bazi(八字结果)。数据库设计:创建一个包含塔罗牌信息的数据库表,如tarot_cards,包含字段:id(塔罗牌ID)、name(塔罗牌名称)、meaning(塔罗牌含义)等。根据操作系统的不同,PHP的安装方法会有所不同。

shanbeizi的博客 2417

RK3588 开启多窗口

这样就支持多窗口了。

tzj1058756809的博客 266

深入JVM类加载机制

类加载的流程以及双亲委派模型

Floating Cat 2万+

分布式系统中的“无状态”和“有状态”详解

有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如 tomcat 中的 session。例如登录:用户登录后,我们把登录者的信息保存在服务端 session 中,并且给用户一个 cookie 值,记录对应的 session。然后下次请求,用户携带 cookie 值来,我们就能识别到对应 session,从而找到用户的信息。

AI天才研究院 2万+

JVM类加载机制深度解析

在深入探讨Java虚拟机(JVM)的运行机制之前,我们首先需要了解JVM的核心知识点之一——加载过程。想象一下,一个复杂的Java应用程序启动时,JVM如何将代码从磁盘上的文件转换成可以在内存中运行的字节码。这个过程看似简单,实则涉及了JVM的多个组件和复杂的逻辑。在当今的软件开发中,内存管理是确保应用程序稳定性和性能的关键。然而,许多开发者往往忽视了JVM的加载过程,这可能导致对JVM内部工作原理的理解不足。

我是Java程序员廖志伟,感谢朋友们的支持! 1126

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

深入解析JVM类加载机制

JVM类加载机制是Java实现跨平台和动态性的核心基础,主要包括加载、链接和初始化三个阶段。加载阶段通过类加载器将类字节码转换为方法区的运行时数据,并生成Class对象;链接阶段验证类安全性,分配静态变量内存并解析符号引用;初始化阶段执行静态代码块和变量赋值。关键机制是双亲委派模型,通过层次化类加载器(启动类、扩展类和应用类加载器)确保类只加载一次,防止核心API被篡改。该模型优先委派父加载器处理请求,保障了Java程序的安全性和稳定性。

2301_80215285的博客 1212

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

JVM类加载机制解析

场景问题:在一个大型分布式系统中,每当启动一个新的服务实例时,系统管理员都会发现服务启动时间较长,且启动过程中资源消耗较大。经过分析,发现这是由于系统中的类加载机制不够高效,导致大量的类需要被重复加载,从而增加了启动时间和资源消耗。为了解决这个问题,我们需要深入了解JVM的类加载机制。知识点重要性:介绍JVM核心知识点之加载:概述的重要性在于,它是理解JVM运行时行为的基础。类加载是JVM执行Java程序的第一步,它决定了哪些类会被加载到JVM中,以及如何加载这些类。

MoneyHacksPro的博客 1111

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

JVM面试 类加载机制

JVM的类加载机制 一、JVM的运行机制 JVM 是用于运行Java字节码的虚拟机,包括一套字节码指令集,一组程序寄存器,一个虚拟机栈,一个虚拟机堆,一个方法区和一个垃圾回收器。JVM运行在操作系统之上,不与硬件设备直接交互; Java程序具体运行过程: JAVA源文件被编译器编译成字节码文件 JVM将字节码文件编译成相应操作系统的机器码 机器码调用相应操作系统的本地方法库执行相应的方法 二、JVM的类加载机制 1.JVM的类加载阶段 加载 验证 准备 解析 初始化 在类初始化完成后就可以使用该

分享技术,享受生活 7389

JVM 类加载机制解析

场景:需要从非标准来源加载类(如加密文件、网络传输、数据库)。实现步骤继承类;重写方法(推荐)或方法(不推荐,可能破坏双亲委派模型);在findClass中通过将字节流转换为Class对象。示例代码@Overridetry {JVM 类加载机制是 Java 跨平台和动态性的基石,其核心流程(加载→验证→准备→解析→初始化)和双亲委派模型确保了类加载的安全性和唯一性。理解类加载机制有助于解决类冲突、自定义类加载等实际问题,也是深入理解 Java 虚拟机的关键一步。

hqxstudying的博客 1228

JVM 类加载机制

类加载器作用范围加载路径启动类加载器(Bootstrap)加载 JVM 核心类(比如JAVA_HOME/jre/lib/rt.jar 等核心 jar扩展类加载器(Extension)加载 JVM 扩展类JAVA_HOME/jre/lib/ext 目录应用程序类加载器(App)加载用户编写的类和第三方 jar应用 classpath 路径要不要我帮你补充一份类加载器工作流程示意图,直观展示双亲委派模型的执行顺序?当前文件内容过长,豆包只阅读了前 33%。

2301_78125917的博客 1191

JVM-类加载机制

对于Java开发者来说,我们每天都在编写.java文件,然后通过编译器将其编译成.class文件。那么,这些.class文件是如何被加载到Java虚拟机(JVM)中,并最终变成我们可以在程序中使用的对象和方法的呢?这个过程就是类加载(Class Loading)。理解类加载机制,不仅仅是满足技术好奇心,更是解决实际问题的关键。你是否遇到过或异常?是否好奇为什么 Tomcat 等Web容器可以隔离不同应用的类库?是否想了解热部署、模块化等高级特性是如何实现的?这些问题的答案,都深藏在类加载机制之中。类加载。

左灯右行的爱情 1695

JVM 的类加载机制原理

文件加载到内存中,并将其转换为 JVM 可以使用的 Java 类型。类加载过程包括加载、链接(验证、准备、解析)和初始化几个阶段。类加载器遵循双亲委派模型,保证了类加载的顺序和安全性。文件(包含 Java 字节码)加载到内存,并对其进行校验、解析、初始化,最终转换为 JVM 可以直接使用的 Java 类型的过程。JVM 的类加载机制是指 JVM 将。JVM 的类加载机制负责将。

专注于技术分享 1274
上一篇: 初试AspectJ切面编程(AOP)
下一篇: 怎么样优化 Java 内存管理,防止“GC”错误
程序员职业指南
博客等级 码龄4年 2136粉丝 960原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值