JVM之【类加载机制】

news2024/11/16 17:47:51

一、类加载过程

1. 加载(Loading)

工作内容

  • 通过类的全限定名来获取定义此类的二进制字节流
    • JVM首先会调用类加载器的findClass方法来找到类文件的路径,通常从文件系统、JAR包、网络、数据库等来源获取类文件。
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
    • 将字节流解析为JVM内部的Class对象,包括类的基本信息,如类名、父类名、接口、字段、方法等。
  • 在内存中生成一个代表这个类的Class对象
    • 在堆内存中生成一个Class对象,用来封装方法区中的类信息,并且将其与类的二进制字节流关联起来。

作用

  • 将类的字节码从外部存储加载到JVM内存中,并为其创建对应的Class对象,作为后续处理的基础。

2. 验证(Verification)

工作内容

  • 文件格式验证
    • 检查字节流的格式是否符合Class文件规范,例如是否以魔数0xCAFEBABE开头、主次版本号是否在支持的范围内等。
  • 元数据验证
    • 检查类的元数据信息是否合法,如类的版本号、父类是否存在、类名是否合法等。
  • 字节码验证
    • 通过数据流和控制流分析,确保字节码指令不会造成类型错误、栈溢出、下溢等问题。例如,确保方法的局部变量表和操作数栈在任何时候都具有正确的类型和大小。
  • 符号引用验证
    • 确保类中的符号引用(例如对其他类、方法、字段的引用)是合法的,能够在运行时解析。例如,确保引用的类、字段、方法确实存在,方法的参数和返回类型正确等。

作用

  • 确保被加载的类是合法和安全的,防止恶意代码通过非法的字节码破坏JVM的稳定性,保护运行时环境。

3. 准备(Preparation)

工作内容

  • 为类的静态变量分配内存
    • 在方法区中为类的所有静态变量分配内存(不包括用final修饰的常量,因为常量在编译期已经被分配在常量池中)。
  • 将静态变量初始化为默认值
    • 根据Java语言规范,基本数据类型(如int、float等)的静态变量初始化为其默认值(如0、0.0等),引用类型变量初始化为null。例如,public static int a;初始化为0,public static String s;初始化为null。

作用

  • 为类的静态变量分配和初始化内存,确保类在初始化之前有正确的初始状态。

4. 解析(Resolution)

工作内容

  • 将常量池中的符号引用转换为直接引用
    • 符号引用是一组符号来描述目标(如类、字段、方法)的符号名称,直接引用是指向目标的实际内存地址或运行时数据结构。例如,将方法调用的符号引用转换为指向具体方法实现的直接引用。
  • 具体解析过程
    • 类或接口的解析:将符号引用转换为具体的类或接口。
    • 字段解析:将符号引用转换为字段的内存地址。
    • 类方法和接口方法解析:将符号引用转换为方法的直接调用地址。

作用

  • 将符号引用解析为可以直接使用的内存地址或运行时数据结构,提高运行时的访问速度和效率。

5. 初始化(Initialization)

工作内容

  • 执行类构造器方法
    • JVM自动收集类中的所有静态变量的赋值动作和静态代码块,合并生成类的构造器<clinit>方法(编译器自动收集,不需手工编写)。<clinit>方法负责初始化静态变量和执行静态代码块。如果没有,就不会生成。

    • 例如:

      public class Test {
          static int x = 10;
          static {
              x = 20;
          }
      }
      

      在初始化阶段会执行静态变量x的赋值和静态代码块,将x初始化为20

      public class Test {
      	static {
              x = 20;
          }
          static int x = 10;
      }
      

      指令语句按照源文件中出现的顺序执行。此处不会报错,将x初始化为20,然后在初始化为10;

  • 确保父类的初始化
    • 在初始化一个类之前,首先需要确保其父类已经初始化。例如,在初始化子类之前,JVM会递归地先初始化其所有父类。
  • 初始化静态变量
    • 根据程序中的静态变量赋值语句进行赋值。
  • 执行静态代码块
    • 执行类中的静态代码块,进行必要的初始化操作。

作用

  • 初始化类的静态变量和静态代码块,确保类在使用之前已经完成必要的初始化工作,保证程序的正确性和一致性。

通过这五个阶段,JVM确保类从加载到使用的全过程中,每一步都经过严格的检查和处理,从而保证了Java程序的安全性、稳定性和正确性。


二、 类与类加载器

通过一个类的全限定名来获取描述该类的二进制字节流

1.类加载器(Class Loader)

定义

  • 类加载器是负责在运行时加载类的Java组件。它从不同的源(如文件系统、网络等)加载类的字节码,并将其转换为JVM可以执行的类对象。

工作原理

  • 类加载器遵循双亲委派模型。每个类加载器都有一个父类加载器,当需要加载一个类时,类加载器会首先委托父类加载器进行加载,如果父类加载器无法找到该类,它才会尝试自己加载。

2.类加载器的分类

狭义上讲,JVM支持两种类型的加载器:引导类加载器和自定义类加载器。自定义类加载器指的是:派生于抽象类ClassLoader的类加载器。

JVM中有多种类加载器,根据加载的优先级和作用范围,可以分为以下几类:

  1. 引导类加载器(Bootstrap Class Loader)

    • 位置:JVM内部实现,通常由C++语言实现。
    • 加载范围:负责加载JDK核心类库(如java.lang.*java.util.*等)和核心JAR包。还要加载扩展类加载器和程序类加载器
    • 特点:是JVM启动时第一个被加载的类加载器,没有父类加载器。
  2. 扩展类加载器(Extension Class Loader)

    • 位置:由Java实现,通常是sun.misc.Launcher$ExtClassLoader的实例。派生于ClassLoader类
    • 加载范围:加载扩展目录lib/ext下的类库(JAR文件)。
    • 父类加载器:引导类加载器。
  3. 应用程序类加载器(Application/System Class Loader)

    • 位置:由Java实现,通常是sun.misc.Launcher$AppClassLoader的实例。派生于ClassLoader类
    • 加载范围:加载用户类路径(classpath)上的类,我们开发的类都是由它来完成加载的。
    • 父类加载器:扩展类加载器。
  4. 自定义类加载器(Custom Class Loader)

    • 位置:用户可以自定义实现java.lang.ClassLoader类。
    • 加载范围:根据用户需求自定义加载范围,可以从网络、数据库等非标准路径加载类。
    • 父类加载器:可以是应用程序类加载器或其他类加载器。

3.类加载器的双亲委派模型

工作流程

  • 委派机制:当一个类加载器接到加载类的请求时,它不会自己尝试加载这个类,而是将请求委派给父类加载器。
  • 自顶向下:如果父类加载器找不到目标类,才会由当前类加载器自己进行加载。

优点

  • 安全性:确保核心类库不会被用户自定义的类库替代,避免了核心类库被篡改的风险。
  • 一致性:确保同一个类在不同的类加载器环境中只有一个版本,避免了类的重复加载和版本冲突。

注意:不同类加载器加载同一个类

比较结果

  • 如果不同的类加载器加载同一个类(即使类名和包名完全相同),它们在JVM中也会被视为不同的类。
  • 原因是类的唯一性不仅由类名决定,还由加载它的类加载器决定。

示例

ClassLoader classLoader1 = new CustomClassLoader();
ClassLoader classLoader2 = new CustomClassLoader();

Class<?> class1 = classLoader1.loadClass("com.example.MyClass");
Class<?> class2 = classLoader2.loadClass("com.example.MyClass");

boolean areClassesEqual = class1.equals(class2); // 结果为false

在上述示例中,虽然class1class2的全限定名相同,但是由于它们是由不同的类加载器加载的,因此它们被认为是不同的类,areClassesEqual结果为false


三、双亲委派模型

定义
JVM的双亲委派机制(Parent Delegation Model)是一种类加载机制,它确保类加载请求按照层次结构从子类加载器向父类加载器递归传递,直到找到合适的类加载器进行加载。如果父类加载器无法加载该类,子类加载器才会尝试加载。

1.工作原理

工作流程

  1. 类加载请求:当一个类加载器接到一个类加载请求时,它首先不会直接尝试加载该类。
  2. 委派父类加载器:它会将这个类加载请求委派给父类加载器(如果存在)。
  3. 递归委派:这种委派是递归的,每个父类加载器又会将请求向上委派,直至顶层的引导类加载器(Bootstrap Class Loader)。
  4. 类加载:如果引导类加载器能够加载该类,则加载过程结束。如果引导类加载器无法加载该类,则请求会依次返回到下层的类加载器,最终由最初的类加载器尝试加载该类。

具体过程

  • 启动类加载器(Bootstrap ClassLoader):它是整个加载体系的顶层,由JVM实现,用于加载核心类库(如rt.jar)。
  • 扩展类加载器(Extension ClassLoader):它从JVM的扩展目录(如lib/ext目录)加载类。
  • 应用程序类加载器(Application ClassLoader):它从classpath指定的路径加载应用程序的类。
  • 自定义类加载器(Custom ClassLoader):用户可以根据需要创建自己的类加载器,通常用于加载非标准路径的类。
    在这里插入图片描述

2.意义和作用

1. 安全性

  • 防止核心类库被篡改:核心类库(如java.lang.*java.util.*等)由引导类加载器加载,不会被用户自定义类加载器篡改。例如,用户不能通过自定义类加载器替换系统核心类,这样可以防止恶意代码攻击和破坏JVM的安全性。

2. 避免类重复加载

  • 类加载的一致性:同一个类在不同的加载器环境中应该只有一个版本。双亲委派机制通过向上委派确保类只会被加载一次,避免重复加载导致的类不一致问题。
  • 内存效率:减少类的重复加载,可以节省内存,提高内存利用率。

3. 模块化设计

  • 模块解耦:双亲委派机制允许不同模块(如核心类库、扩展类库、应用类库)使用不同的类加载器,模块之间通过委派机制进行类加载请求的传递,使得模块之间更加独立和解耦。

3.实际应用中的场景

1. 应用服务器和框架

  • 例如Tomcat、Spring等:它们使用自定义类加载器加载应用程序的类和库,而核心类库和框架类库则由应用类加载器或其父类加载器加载。这种设计保证了框架和应用程序的独立性和安全性。

2. 插件系统

  • 例如Eclipse、IDEA等:插件系统通常需要动态加载和卸载插件。通过自定义类加载器,插件可以在隔离的类加载器环境中运行,不会影响其他插件或主程序。

通过上述机制和设计,双亲委派机制确保了Java类加载过程的安全性、一致性和模块化,使得Java应用程序能够在复杂的类加载环境中稳定运行。


四、其他

1、JVM必须知道一个类型是由启动加载器加载的还是由用户类加载器加载的。如果一个类型是由用户类加载器加载的,那么JVM会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中。当解析一个类型到另一个类型的引用的时候,JVM需要保证这两个类型的类加载器是相同的。
2、Java程序对类的使用方式分为:主动使用和被动使用
主动使用,又分为七种情况:

  1. 创建类的实例
  2. 访问某个类或接口的静态变量,或者对该静态变量赋值
  3. 调用类的静态方法
  4. 反射(比如:Class.forName(“com.atguigu.Test"))初始化一个类的子类
  5. Java虚拟机启动时被标明为启动类的类
  6. JDK 7开始提供的动态语言支持:
  7. java.lang.invoke.MethodHandle实例的解析结果REF getstatic、REF putstatic、REF invokestatic句柄对应的类没有初始化,则初始化

除了以上七种情况,其他使用Java类的方式都被看作是对类的被动使用都不会导致类的初始化。

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

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

相关文章

揭秘Markdown:轻松掌握基础语法,让你的写作更高效、优雅!

文章目录 前言1.标题1.1 使用 和 - 标记一级和二级标题1.2 使用 # 号标记 2.段落格式2.1 字体2.2 分割线2.3 删除线2.4 下划线2.5 脚注 3.列表3.1 无序列表3.2 有序列表3.3 列表嵌套 4.区块4.1 区块中使用列表4.2 列表中使用区块 5.代码代码区块 6.链接7.图片8.表格9.高级技巧…

C#对文件进行批量重命名或者对某个单独的文件进行改名

目录 一、FolderBrowserDialog 二、OpenFileDialog 三、Path 四、ui设计 五、代码部分 一、FolderBrowserDialog FolderBrowserDialog是一个用于选择文件夹的对话框控件&#xff0c;可以在windows Forms应用程序中使用。使用它可以让用户选择一个文件夹&#xff0c;并返…

闲话 .NET(6):.NET Core 各个版本的特性

前言 之前我们聊了一下 .NET Core 有哪些优势&#xff0c;.NET Core 发展非常迅速&#xff0c;不过短短几年&#xff0c;.NET Core 已经发布 .NET 8 了&#xff0c;基本上保持了一年一个版本的速度&#xff0c;每个版本都有自己的独有特性&#xff0c;下面我们来简单的盘点一下…

从零实现Llama3中文版

1.前言 一个月前&#xff0c;Meta 发布了开源大模型 llama3 系列&#xff0c;在多个关键基准测试中优于业界 SOTA 模型&#xff0c;并在代码生成任务上全面领先。 此后&#xff0c;开发者们便开始了本地部署和实现&#xff0c;比如 llama3 的中文实现、llama3 的纯 NumPy 实现…

【机器学习300问】100、怎么理解卷积神经网络CNN中的池化操作?

一、什么是池化&#xff1f; 卷积神经网络&#xff08;CNN&#xff09;中的池化&#xff08;Pooling&#xff09;操作是一种下采样技术&#xff0c;其目的是减少数据的空间维度&#xff08;宽度和高度&#xff09;&#xff0c;同时保持最重要的特征并降低计算复杂度。池化操作不…

高速数据采集与传输(一):ADC08D500调研

前言&#xff1a;高速ADC数据采集的应用和开发&#xff0c;涉及的技术面非常的广泛&#xff0c;后续阶段博主将尝试以纯项目开发的形式做一次专题技术分享&#xff0c;将基于高速数据采集的相关内容进行一系列的技术文档更新。博主全凭兴趣在更新和总结&#xff0c;很难做到一直…

AI预测体彩排3采取888=3策略+和值012路一缩定乾坤测试5月26日预测第2弹

今天继续基于8883的大底进行测试&#xff0c;昨天的预测已成功命中&#xff01;今天继续测试&#xff0c;按照排三前面的规律&#xff0c;感觉要出对子了&#xff0c;所以本次预测不再杀对子&#xff0c;将采用杀一个和尾来代替。好了&#xff0c;直接上结果吧~ 首先&#xff0…

软考结束。有什么要说的

1. 竟然是机试&#xff0c;出乎我意料。是 考试机构觉得笔试成本高了么。这次的考试是机试&#xff0c;相比以往有所不一样。感言是不是以后都会在固定地点考试也说不准。 2. 遇到年轻人。 这次旁边的一个女同学第一次参加&#xff0c;还像我询问了一些关于软考的事。我是有…

14 vue学习:透传Attributes

Attributes 继承 “透传 attribute”指的是传递给一个组件&#xff0c;却没有被该组件声明为 [props]或 [emits]的 attribute 或者 v-on 事件监听器。最常见的例子就是 class、style 和 id。 当一个组件以单个元素为根作渲染时&#xff0c;透传的 attribute 会自动被添加到根元…

vcpkg环境配置

vcpkg 使用linux相关库&#xff0c;设置环境变量VCPKG_ROOT&#xff0c;设置cmake工具链$VCPKG_ROOT/scripts\buildsystems\vcpkg.cmake set VCPKG_DEFAULT_TRIPLETx64-windows .\vcpkg.exe install fftw3 freetype gettext glibmm gtkmm libjpeg-turbo libpng libxmlpp libs…

pytest框架用例命名规则详解

pytest 测试用例的命名规则是为了确保 pytest 能够正确地识别和执行测试用例。 以下是关于 pytest 测试用例命名规则的详细解释&#xff1a; 1 单个测试文件以‘test_’开头或者以‘_test’结尾 比如我们创建test_case1.py case2_test.py文件。 2 单个测试文件中&#xff0c…

PLC_博图系列☞R_TRIG:检测信号上升沿

PLC_博图系列☞R_TRIG&#xff1a;检测信号上升沿 文章目录 PLC_博图系列☞R_TRIG&#xff1a;检测信号上升沿背景介绍R_TRIG&#xff1a; 检测信号上升沿说明参数示例 关键字&#xff1a; PLC、 西门子、 博图、 Siemens 、 R_TRIG 背景介绍 这是一篇关于PLC编程的文章&a…

服务器安全审计: chkrootkit 和 rkhunter 详解

chkrootkit 和 rkhunter 是两个广泛使用的安全工具,用于检测系统是否被Rootkit或其他恶意软件感染。本文将详细说明这两个工具的使用方法及如何解释检测结果。 1. chkrootkit 1.1. 安装 chkrootkit 在CentOS上安装 chkrootkit 可以使用以下命令: yum install chkrootkit -…

SpringMVC:创建一个简单的SpringMVC框架S

目录 一、框架介绍 两个重要的xml文件 SpringMVC执行流程 二、Vscode搭建SpringMVC框架 1、maven创建webapp原型项目 2、pom.xml下添加springmvc的相关依赖 3、在web.xml配置 4、springmvc.xml的配置 5、编写Controller控制器类 6、 编写JSP界面 7、项目结构图 一…

$subcribe的使用

$subcribe的使用 只要是store都有$subscribe函数&#xff0c;是订阅的意思&#xff0c;可以监测到store中数据的变化 使用$subscribe函数可以实现刷新不丢失&#xff0c;将数据保存到浏览器的本地存储中&#xff0c;每次进入页面都使用localStorage的数据填充页面

springboot带颜色的日志输出

话不多说直接贴全部代码在作解释 <?xml version"1.0" encoding"utf-8"?> <configuration><property name"pattern" value"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg %n"/><propert…

MySQL数据库基础:使用、架构、SQL语句、存储引擎

文章目录 什么是数据库CS模式 基本使用安装链接服务器服务器、数据库、表关系简单使用数据库在Linux下的体现 MySQL架构连接器层客户端层服务层存储引擎层物理存储层 SQL分类存储引擎 什么是数据库 mysql&#xff1a;数据库服务的客户端mysqld&#xff1a;数据库服务的服务器端…

LVS精益价值管理系统 LVS.Web.ashx SQL注入漏洞复现

0x01 产品简介 LVS精益价值管理系统是杭州吉拉科技有限公司研发的一款专注于企业精益化管理和价值流优化的解决方案。该系统通过集成先进的数据分析工具、可视化的价值流映射技术和灵活的流程改善机制,帮助企业实现高效、低耗、高质量的生产和服务。 0x02 漏洞概述 LVS精益…

【云原生】Kubernetes-----POD资源限制与探针机制

目录 引言 一、资源限制 &#xff08;一&#xff09;基本定义 &#xff08;二&#xff09;资源单位 1.CPU资源 2.内存资源 &#xff08;三&#xff09;请求与限制 &#xff08;四&#xff09;定义方式 1.编写yaml文件 2.查看资源情况 &#xff08;五&#xff09;资源…

【顶刊新文】nature plants|植物高度作为高山碳固存和生态系统对变暖响应的指标

文章简介 论文名称&#xff1a;Plant height as an indicator for alpine carbon sequestration and ecosystem response to warming&#xff08;植物高度作为高山碳固存和生态系统对变暖响应的指标&#xff09; 第一作者及单位&#xff1a;Quan Quan&#xff08;中国科学院地…