JVM之【类加载机制】

一、类加载过程

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.mzph.cn/pingmian/16356.shtml

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

相关文章

Installing Tinyproxy on CentOS 7 测试可用

Installing Tinyproxy on CentOS 7 For RHEL/CentOS 7 systems, Tinyproxy is part of EPEL (Extra Packages for Enterprise Linux). Install EPEL on CentOS 7 yum install epel-release -y yum update -y Install Tinyproxy on CentOS 7 yum install tinyproxy -y 编辑…

Android单元测试实践

一、基础概念 按照Google官方建议,Android测试体系应该参照测试金字塔架构(如下图所示),App应该包含三类测试(即小型、中型和大型测试)。 图片 小型测试是指单元测试,用于验证应用的行为,一次验证一个类。中型测试是指集成测试,用于验证模块内堆栈级别之间的交互或相…

yolov8报警图片写入minio服务器 Rabbitmq发送地址

OSError [WinError 1455]页面文件太小&#xff0c;无法完成操作”解决方案“_深度学习_yangshejun-GitCode 开源社区 (csdn.net) python对RabbitMQ的简单使用_python rabbitmq-CSDN博客 【Windows安装RabbitMQ详细教程】_rabbitmq windows-CSDN博客 Windows 10安装Minio 文件…

CentOS Stream 9 vsftpd本地用户设置

1、使用yum指令下载vsftpd yum install vsftpd2、创建wu用户&#xff0c;为ftp组的成员 useradd -g ftp wu3、设置一个密码 echo 1 |passwd --stdin wu4、修改本地用户目录的权限&#xff08;所有者为wu用户&#xff09; chown -R wu /data/wu5、创建本地用户的信息数据存放…

mysql 函数 GROUP_CONCAT 踩坑记录,日志:Row 244 was cut by GROUP_CONCAT()

mysql 函数 GROUP_CONCAT 踩坑记录&#xff0c;报错&#xff1a;Row 244 was cut by GROUP_CONCAT 结论&#xff1a;个人建议还是放在内存中拼接吧~db日志信息&#xff1a;Row 244 was cut by GROUP_CONCAT())根本原因&#xff1a;拼接的字符串长度超过 group_concat_max_len […

uni-app实现页面之间的跳转传参(八)

界面之间的参数传递在 开发中经常会用到,这节主要将一下uni-app开发应用是的传参情况。如下图所示,我的一级界面将点检分成三类:日点检、周点检和年保养;在点击相应的会导航到相应的功能。 在uni-app中常用的方法有uni.navigateTo(OBJECT)、uni.redirectTo(OBJECT);简单的…

实时通信的方式——WebRTC

文章目录 基于WebRTC实现音视频通话P2P通信原理如何发现对方&#xff1f; 不同的音视频编解码能力如何沟通&#xff1f;&#xff08;媒体协商SDP&#xff09;如何联系上对方&#xff1f;&#xff08;网络协商&#xff09; 常用的API音视频采集getUserMedia核心对象RTCPeerConne…

Web开发学习总结

学习路线 Web 全球广域网&#xff0c;也称为万维网(www World Wide Web)&#xff0c;能够通过浏览器访问的网站 初识Web前端 Web标准也称为网页标准&#xff0c;由一系列的标准组成&#xff0c;大部分由W3C(World Wide Web Consortium&#xff0c;万维网联盟)负责制定。三个组…

用例篇

弱网测试 弱网测试的目的是尽可能保证用户体验&#xff0c;关注的关键点包括&#xff1a; 页面响应时间是否可以接受&#xff0c;关注包括哦热启动、冷启动时间、页面切换、前后台切换、首字时间&#xff0c;首屏时间等。页面呈现是否完成一致。超时文案是否符合定义&#xf…

CSS浮动(CSS从入门到精通学习第四天)

css第04天 一、其他样式 1、圆角边框 在 CSS3 中&#xff0c;新增了圆角边框样式&#xff0c;这样我们的盒子就可以变圆角了。 border-radius 属性用于设置元素的外边框圆角。 语法&#xff1a; border-radius:length; 参数值可以为数值或百分比的形式如果是正方形&…

K8S认证|CKA题库+答案| 15. 备份还原Etcd

目录 15、 备份还原Etcd CKA v1.29.0模拟系统 下载试用 题目&#xff1a; 开始操作: 1&#xff09;、切换集群 2&#xff09;、登录master并提权 3&#xff09;、备份Etcd现有数据 4&#xff09;、验证备份数据快照 5&#xff09;、查看节点和Pod状态 6&#xff0…

linux下的openssh简介(centos 8)

目录 1. 简介2. 安装 OpenSSH3. 配置 OpenSSH 服务器3.1 服务器配置文件配置文件的详解 3.2 安全操作——修改 SSH 端口3.3 安全操作——禁止 root 登录3.4 安全操作——密钥认证3.5 安全操作——禁止密码认证 4. 配置 OpenSSH 客户端4.0 常用命令4.0.1 ssh常用命令4.0.2 scp常…

Java进阶学习笔记11——多态

什么是多态&#xff1f; 多态是在继承/实现情况下一种现象&#xff0c;表现为&#xff1a;对象多态和行为多态。 同一个对象&#xff0c;在不同时刻表现出来的不同形态。 多态的前提&#xff1a; 要有继承/实现关系 要有方法的重写 要有父类引用指向子类对象。 多态的具体代码…

使用 LangFuse 意外被挂马!我是怎么恢复系统稳定的?

在使用 LangFuse 过程中,被意外挂马!通过一番折腾服务恢复正常~ 本文将详细介绍应对恶意脚本和进程的完整方案,包括识别、清理、恢复和预防步骤。 阿里云扫到的信息 被执行的 Base64 SUlaQnRTCmV4ZWMgJj4vZGV2L251bGwKSUhDa0hQbmQ9Li8uJChkYXRlfG1kNXN1bXxoZWFkIC1jMjApCl…

Value-Based Reinforcement Learning(1)

Action-Value Functions Discounted Return&#xff08;未来的reward&#xff0c;由于未来存在不确定性&#xff0c;所以未来的reward 要乘以进行打折&#xff09; 这里的依赖actions &#xff0c;和states 这里 Policy Function : &#xff0c;表达了action的随机性 S…

创新实训2024.05.26日志:服务端接口实现——用户开启多个会话

1. 概念图 类似于Kimi&#xff0c;文心一言&#xff0c;chatGPT等市面上主流的大模型&#xff0c;我们的大模型也支持同一个用户的多个会话&#xff0c;并且提供支持联系上下文给出解答的能力。 2. 基于会话的对话 在langchain chatchat这个对langchain框架进行二次封装的第三…

vulnhub靶场之FunBox-8

一.环境搭建 1.靶场描述 Its a box for beginners and can be pwned in the lunch break. This works better with VirtualBox rather than VMware 2.靶场下载 Funbox: Lunchbreaker ~ VulnHub 3.靶场启动 二.信息收集 1.寻找靶场真实IP地址 nmap -sP 192.168.2.0/24 arp-…

html中被忽略的简单标签

1&#xff1a; alt的作用是在图片不能显示时的提示信息 <img src"https://img.xunfei.cn/mall/dev/ifly-mall-vip- service/business/vip/common/202404071019208761.jp" alt"提示信息" width"100px" height"100px" /> 2&#…

嵌入式进阶——震动马达

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 原理图控制分析功能设计 原理图 控制分析 S8050 NPN三极管特性 NPN型三极管的工作原理是基于PN结和PNP型晶体管的工作原理。 当外…

【qt】QTreeWidget 树形组件

QTreeWidget 树形组件 一.什么是树形组件二.界面设计树形组件三.代码实现1.清空2.设置列数3.设置头标签4.添加根目录①QTreeWidgetitem②设置文本③设置图标④添加为顶层目录 5.添加子目录①初始化为父目录②子目录添加到父目录③获取到子目录 四.插入目录1.获取当前选中目录项…