Java 类加载过程和双亲委派模型

2024-05-27 12:44

本文主要是介绍Java 类加载过程和双亲委派模型,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Java 类加载过程概述

在 Java 中,类装载器把一个类装入 Java 虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备、解析
Java类加载过程分为如下步骤:
1.装载( 加载):查找和导入类或接口的二进制数据;通过类的全限定名获取类的二进制字节流并存储在内存中。
2.链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;
校验(Verification):检查导入类或接口的二进制数据的正确性;验证字节流的正确性,包括文件格式、语法正确性等。
准备:给类的静态变量分配并初始化存储空间;为类的静态变量分配内存空间,并初始化为默认值。
解析:将符号引用转成直接引用;将符号引用解析为直接引用,例如将类或接口的全限定名解析为内存地址
初始化(Initialization):激活类的静态变量,初始化 Java 代码和静态 Java 代码块,执行类的初始化代码,包括静态变量的赋值和静态代码块的执行等
上述过程中,加载、验证、准备、解析是类加载的前期准备工作,而初始化阶段是类加载的最后一步,也是类真正被使用前的准备工作。

Java的类加载过程详解

Java的类加载过程是一个重要的概念,它涉及到JVM(Java虚拟机)如何动态地加载Java类到内存中,以便执行其中的代码。这个过程大致可以分为三个阶段:加载(Loading)、链接(Linking)和初始化(Initialization)。

  1. 加载(Loading)
    加载是类加载过程的第一个阶段。在这个阶段,JVM主要完成以下三件事情:
    通过一个类的全限定名来获取定义此类的二进制字节流:这个二进制字节流可以从文件系统、网络或其他来源获取。将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构:在加载阶段,JVM会在方法区中生成一个代表这个类的Class对象,作为方法区这个类的各种数据的访问入口。
    在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
  2. 链接(Linking)
    链接阶段又可以细分为三个阶段:验证(Verification)、准备(Preparation)和解析(Resolution)。
    2.1 验证(Verification)
    验证是确保被加载的类的正确性和安全性的阶段。主要包括以下四个方面的验证:
    文件格式验证:验证字节流是否符合Class文件格式的规范,并且能被当前版本的JVM处理。
    元数据验证:对类的元数据信息进行语义校验(比如这个类是否有父类,指定的父类是否存在等)。字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
    符号引用验证:确保解析动作能正确执行。
    2.2 准备(Preparation)
    准备阶段是为类的静态变量分配内存,并将其初始化为默认值。这里需要注意的是,这个阶段只是为静态变量分配了内存并设置为了默认值,而真正的赋值操作会在初始化阶段进行。
    2.3 解析(Resolution)
    解析阶段是JVM将常量池内的符号引用替换为直接引用的过程。符号引用是一个逻辑引用,它只包含了引用的名称和描述,而没有具体的内存地址。而直接引用则是一个指向具体内存地址的指针或者句柄。
  3. 初始化(Initialization)
    初始化阶段是类加载过程的最后一个阶段。在这个阶段,JVM才真正开始执行类中定义的Java代码(比如静态代码块、为静态变量赋值等)。如果在初始化阶段出现了异常,那么JVM会立即停止对该类的初始化,并且不会再次尝试对该类进行初始化。
    需要注意的是,类的初始化只会被执行一次。如果一个类已经被加载、链接并且初始化过了,那么再次请求加载这个类时,JVM会直接返回代表这个类的Class对象,而不会重新执行类的加载、链接和初始化过程。这就是所谓的“双亲委派模型”中的一部分。

委托模型机制
委托模型机制的工作原理很简单:当类加载器需要加载类的时候,先请示其Parent(即上一层加载器)在其搜索路径载入,如果找不到,才在自己的搜索路径搜索该类。这样的顺序其实就是加载器层次上自顶而下的搜索,因为加载器必须保证基础类的加载。之所以是这种机制,还有一个安全上的考虑:如果某人将一个恶意的基础类加载到jvm,委托模型机制会搜索其父类加载器,显然是不可能找到的,自然就不会将该类加载进来。
我们可以通过这样的代码来获取类加载器
ClassLoader loader = ClassName.class.getClassLoader(); ClassLoader ParentLoader = loader.getParent();
注意一个很重要的问题,就是Java在逻辑上并不存在BootstrapKLoader的实体!因为它是用C++编写的,所以打印其内容将会得到null。

什么是双亲委派模型(Parent-Delegation Model)

双亲委派机制:某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载
所谓双亲委派机制,或者叫父级委托模型,就是指按照类加载器的层级关系JVM 中加载类机制采用的是双亲委派模型,顾名思义,在该模型中,子类加载器收到的加载请求,不会先去处理,而是先把请求委派给父类加载器处理,当父类加载器处理不了时再返回给子类加载器加载;
因为安全。使用双亲委派模型来组织类加载器间的关系,能够使类的加载也具有层次关系,这样能够保证核心基础的 Java 类会被根加载器加载,而不会去加载用户自定义的和基础类库相同名字的类,从而保证系统的有序、安全。

  1. BootStrap ClassLoader 启 动ClassLoader 启动类加载器
  2. Extension ClassLoader 扩 展ClassLoader 扩展类加载器
  3. App ClassLoader 应 ⽤ClassLoader/系 统ClassLoader 应用程序类加载器
  4. Custom ClassLoader ⾃定义ClassLoader 自定义类加载器
    除了BootStrap ClassLoader,每个ClassLoader都有⼀个Parent作为⽗亲。
  5. 自底向上检查类是否已经加载;
  6. 自顶向下尝试加载类。

双亲委派机制:当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。
在这里插入图片描述

Java中的所有类,都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中。在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式装载的,除非我们有特殊的用法,像是反射,就需要显式的加载所需要的类。
Java类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到jvm中,至于其他类,则在需要的时候才加载。这当然就是为了节省内存开销。
Java的类加载器有三个,对应Java的三种类:

Bootstrap Loader // 负责加载系统类 (指的是内置类,像是String,对应于C#中的System类和C/C++标准库中的类)
ExtClassLoader // 负责加载扩展类(就是继承类和实现类)
-AppClassLoader // 负责加载应用类(程序员自定义的类)
好处:安全,避免重复加载
Bootstrap 类加载器负责加载 rt.jar 中的 JDK 类文件,它是所有类加载器的父加载 器 。 Bootstrap 类 加 载 器 没 有 任 何 父 类 加 载 器 , 如 果 你 调 用String.class.getClassLoader() , 会 返 回 null , 任 何 基 于 此 的 代 码 会 抛 出NullPointerException 异常。Bootstrap 加载器被称为初始类加载器。
而 Extension 将加载类的请求先委托给它的父加载器,也就是 Bootstrap,如果没有成功加载的话,再从 jre/lib/ext 目录下或者 java.ext.dirs 系统属性定义的目录下加载类。Extension 加载器由sun.misc.Launcher E x t C l a s s L o a d e r 实现。第三种默认的加载器就是 A p p l i c a t i o n 类加载器了。它负责从 c l a s s p a t h 环境变量中加载某些应用相关的类, c l a s s p a t h 环境变量通常由 − c l a s s p a t h 或 − c p 命令行选项来定义,或者是 J A R 中的 M a n i f e s t 的 c l a s s p a t h 属性。 A p p l i c a t i o n 类加载器是 E x t e n s i o n 类加载器的子加载器。通 s u n . m i s c . L a u n c h e r ExtClassLoader 实现。 第三种默认的加载器就是 Application 类加载器了。它负责从 classpath 环境变量中加载某些应用相关的类,classpath 环境变量通常由-classpath 或-cp 命令行选项来定义,或者是 JAR 中的 Manifest 的 classpath 属性。Application 类加 载 器 是 Extension 类 加 载 器 的 子 加 载 器 。 通 sun.misc.Launcher ExtClassLoader实现。第三种默认的加载器就是Application类加载器了。它负责从classpath环境变量中加载某些应用相关的类,classpath环境变量通常由classpathcp命令行选项来定义,或者是JAR中的Manifestclasspath属性。Application类加载器是Extension类加载器的子加载器。通sun.misc.LauncherAppClassLoader 实现

两种方式来破坏双亲委派模型

第一种,集成ClassLoader抽象类,重写loadClass方法,在这个方法可以自定义要加载的类使用的类加载器。
第二种,使用线程上下文加载器,可以通过java.lang.Thread类的setContextClassLoader()方法来设置当前类使用的类加载器类型。

为什么需要双亲委派模型

在这里,先想一下,如果没有双亲委派,那么用户是不是可以自己定义一个java.lang.Object的同名
类,java.lang.String的同名类,并把它放到ClassPath中,那么类之间的比较结果及类的唯一性将无法
保证,因此,为什么需要双亲委派模型?防止内存中出现多份同样的字节码

这篇关于Java 类加载过程和双亲委派模型的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1007515

相关文章

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2

Spring Cloud LoadBalancer 负载均衡详解

《SpringCloudLoadBalancer负载均衡详解》本文介绍了如何在SpringCloud中使用SpringCloudLoadBalancer实现客户端负载均衡,并详细讲解了轮询策略和... 目录1. 在 idea 上运行多个服务2. 问题引入3. 负载均衡4. Spring Cloud Load

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

在 Spring Boot 中使用 @Autowired和 @Bean注解的示例详解

《在SpringBoot中使用@Autowired和@Bean注解的示例详解》本文通过一个示例演示了如何在SpringBoot中使用@Autowired和@Bean注解进行依赖注入和Bean... 目录在 Spring Boot 中使用 @Autowired 和 @Bean 注解示例背景1. 定义 Stud

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应