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

面试小札:Java的类加载过程和类加载机制。

Java类加载过程

 

加载(Loading)

这是类加载过程的第一个阶段。在这个阶段,Java虚拟机(JVM)主要完成三件事:

通过类的全限定名来获取定义此类的二进制字节流。这可以从多种来源获取,如本地文件系统(.class文件)、网络(如从远程服务器下载字节码)、动态生成字节码(如使用字节码生成库)等。

将字节流所代表的静态存储结构转换为方法区(在JDK 1.8之后,元数据存储在本地内存的元空间Metaspace中)中的运行时数据结构。

在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口。这个 Class 对象在堆内存中,它就像是一面镜子,反射出类在方法区中的结构。

验证(Verification)

目的是确保被加载的类的字节码是合法的、符合Java虚拟机规范的。它主要包括四个验证阶段:

文件格式验证:验证字节流是否符合Class文件格式的规范,例如是否以魔数( 0xCAFEBABE )开头,主次版本号是否在当前JVM支持的范围内等。

元数据验证:对字节码描述的信息进行语义分析,以保证其符合Java语言规范,例如检查这个类是否有父类(除了 java.lang.Object ),是否继承了不允许继承的类(如 final 类)等。

字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。例如检查操作数栈的数据类型是否和指令的操作码相匹配,跳转指令是否会跳转到方法体以外的字节码指令上。

符号引用验证:在解析阶段将符号引用转换为直接引用的时候,对符号引用进行验证。这个阶段主要是确保解析行为能正常执行,比如检查符号引用中的类、字段、方法是否确实存在等。

准备(Preparation)

这一阶段是为类的静态变量(被 static 修饰的变量)分配内存并设置默认初始值。例如对于 public static int value = 123; ,在准备阶段, value 会被初始化为0(基本数据类型的默认值),而不是123。这里需要注意的是,这一阶段不会执行任何Java代码,仅仅是为变量分配内存和设置默认值。

解析(Resolution)

这是虚拟机将常量池内的符号引用替换为直接引用的过程。符号引用是一种对目标的描述,例如一个类的全限定名、方法的名称和描述符等。直接引用是指向目标的指针、相对偏移量或者能间接定位到目标的句柄。例如,在调用一个方法时,需要将方法的符号引用解析为实际内存中的方法入口地址(直接引用)。这个过程主要针对类或接口、字段、类方法、接口方法等符号引用进行解析。

初始化(Initialization)

这是类加载过程的最后一步,也是真正开始执行类中定义的Java程序代码的阶段。这个阶段主要是执行类构造器 <clinit>() 方法。 <clinit>() 方法是由编译器自动收集类中的所有类变量(静态变量)的赋值动作和静态语句块( static{} )中的语句合并产生的。JVM会保证这个方法在多线程环境下被正确地加锁和同步,即只有一个线程能够执行这个类的 <clinit>() 方法。

 

 

Java类加载机制

 

双亲委派模型(Parents Delegation Model)

工作原理:当一个类加载器收到类加载请求时,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成。只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载。这种层次结构就像一个树状结构,最顶层是启动类加载器(Bootstrap ClassLoader),它主要负责加载 <JAVA_HOME>/lib 目录下的类库,如 rt.jar 等核心库;然后是扩展类加载器(Extension ClassLoader),负责加载 <JAVA_HOME>/lib/ext 目录下的类库;最后是应用程序类加载器(Application ClassLoader),负责加载用户类路径( classpath )下的类。

优势:

避免类的重复加载。因为父加载器已经加载过的类,子加载器就不需要再次加载了。

保证了Java核心库的安全性。例如,用户自定义了一个 java.lang.String 类,由于双亲委派模型,这个类不会被加载,因为启动类加载器会首先加载Java核心库中的 java.lang.String 类,这样就防止了用户恶意篡改核心类库的行为。

自定义类加载器(Custom Class Loader)

在某些情况下,我们可能需要自定义类加载器。例如,从加密的字节码文件中加载类,或者从非标准的位置(如数据库)加载类。要实现自定义类加载器,需要继承 java.lang.ClassLoader 类,并重写 findClass 方法。在 findClass 方法中,需要实现从自定义的源获取字节码数据,然后调用 defineClass 方法将字节码转换为 Class 对象。通过自定义类加载器,我们可以更加灵活地控制类的加载过程,满足特殊的应用需求。

http://www.lryc.cn/news/489796.html

相关文章:

  • Spring 上下文对象
  • Wireshark抓取HTTPS流量技巧
  • 测试人员--如何区分前端BUG和后端BUG
  • 【Vue】指令扩充(指令修饰符、样式绑定)
  • Ubuntu20.04 Rk3588 交叉编译ffmpeg7.0
  • HTML常用表格与标签
  • 网络安全与加密
  • MySQL数据库-索引的介绍和使用
  • 【图像去噪】论文精读:Pre-Trained Image Processing Transformer(IPT)
  • Java SE 与 Java EE:基础与进阶的探索之旅
  • ssm旅游推荐系统的设计与开发
  • 【人工智能】用Python和NLP工具构建文本摘要模型:使用NLTK和spaCy进行自然语言处理
  • 51c大模型~合集76
  • 资源控制器--laravel进阶篇
  • 对象:是什么,使用,遍历对象,内置对象
  • 设计模式:4、命令模式(双重委托)
  • DataWorks快速入门
  • EasyExcel并行导出多个excel文件并压缩下载
  • 圣诞节秘诀
  • 亚信安全发布《2024年第三季度网络安全威胁报告》
  • Long noncoding RNAs and humandisease
  • 嵌入式AI之rknn yolov5初探
  • 《Vue零基础入门教程》第三课:起步案例
  • 深入浅出C#编程语言
  • 游戏盾 :在线游戏的终极防护屏障
  • 工作中的问题记录笔记
  • 加载指定会话最近消息
  • 基于tensorflow使用VGG16实现猫狗识别
  • 第18章 EXISTS 与 NOT EXISTS 关键字
  • Windows多JDK版本管理工具JVMs