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类的方式都被看作是对类的被动使用都不会导致类的初始化。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式进阶——数码管2

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 驱动封装封装的一些疑问数字走马灯实现扩展知识 驱动封装 根据前面的内容可以将代码进行封装&#xff0c;封装后作为一个独立的整…

Flutter仿照微信实现九宫格头像

一、效果图 2、主要代码 import dart:io; import dart:math;import package:cached_network_image/cached_network_image.dart; import package:flutter/material.dart;class ImageGrid extends StatelessWidget {final List<String> imageUrls; // 假设这是你的图片URL…

山东大学软件学院数据库实验1-9(全部)

目录 前言 实验代码 实验一 1-1 1-2 1-3 1-4 1-5 1-6 实验二 2-1 2-2 2-3 2-4 2-5 2-6 2-7 2-8 2-9 2-10 实验三 3-1 3-2 3-3 3-4 3-5 3-6 3-7 3-8 3-9 3-10 实验四 4-1 4-2 4-3 4-4 4-5 4-6 4-7 4-8 4-9 4-10 实验五 5-1…

代码随想录算法训练营第三十六天 | 1005.K次取反后最大化的数组和、134.加油站、135.分发糖果

目录 1005.K次取反后最大化的数组和 思路 代码 代码 134.加油站 思路 代码 135.分发糖果 思路 代码 1005.K次取反后最大化的数组和 本题简单一些&#xff0c;估计大家不用想着贪心 &#xff0c;用自己直觉也会有思路。 代码随想录 思路 直觉&#xff0c;直接写&…

<学习笔记>从零开始自学Python-之-实用库篇(一)-pyscript

由Anaconda创建的PyScript是一项实验性的但很有前途的新技术&#xff0c;它使python运转时在支撑WebAssembly的浏览器中作为一种脚本言语运用。 每个现代常用的浏览器现在都支撑WebAssembly&#xff0c;这是许多言语&#xff08;如C、C和Rust&#xff09;能够编译的高速运转时…

K8S/ hpa分享

在 Kubernetes 中&#xff0c;HorizontalPodAutoscaler 自动更新工作负载资源 &#xff08;例如 Deployment 或者 StatefulSet&#xff09;&#xff0c; 目的是自动扩缩工作负载以满足需求。 hpa的使用本身还是很简单的 示例如下&#xff1a; 官网示例 apiVersion: apps/v1 k…

基础—SQL—DDL—建表、查表、修改表以及总结

一、DDL—表—创建表与数据类型的设定 &#xff08;1&#xff09;要求 根据需求创建表(设计合理的数据类型、长度) 设计一张员工信息表&#xff0c;要求如下: 1、编号&#xff08;纯数字) 2、员工工号(字符串类型&#xff0c;长度不超过10位) 3、员工姓名&#xff08;字符串类…

设计模式10——装饰模式

写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用&#xff0c;主要是下面的UML图可以起到大作用&#xff0c;在你学习过一遍以后可能会遗忘&#xff0c;忘记了不要紧&#xff0c;只要看一眼UML图就能想起来了。同时也请大家多多指教。 装饰模式 是一种行为型模式。…

笔记88:LeetCode_134_加油站

前言&#xff1a; 前言1&#xff1a;这个题的题目条件给的不太严谨&#xff0c;题目描述中说“如果存在解&#xff0c;则保证它是唯一的”&#xff0c;通过我的实践&#xff0c;我发现这句话的意思其实是本题的所有样例只有两种情况&#xff0c;无解/有唯一解&#xff1b;而不可…

【Spring】认识 Spring AOP

认识 Spring AOP 1.什么是 AOP2.AOP 中的概念3.用 AOP 方式管理日志3.1 编写 AOP 日志注解类3.2 编写控制器用于测试 1.什么是 AOP AOP&#xff08;Aspect Oriented Program&#xff0c;面向切面编程&#xff09;把业务功能分为核心、非核心两部分。 核心业务功能&#xff1a…

tcpdump源码分析

进入tcpdump.c&#xff08;函数入口&#xff09;之前&#xff0c;先看一些头文件netdissect.h里定义了一个数据结构struct netdissect_options来描述tcdpump支持的所有参数动作&#xff0c;每一个参数有对应的flag, 在tcpdump 的main 里面&#xff0c; 会根据用户的传入的参数来…

构建高效的在线培训机构CRM应用架构实践

在当今数字化时代&#xff0c;在线培训已成为教育行业的重要趋势之一。为了提供更好的学习体验和管理服务&#xff0c;在线培训机构需要构建高效的CRM&#xff08;Customer Relationship Management&#xff09;应用架构。本文将探讨在线培训机构CRM应用架构的设计与实践。 一、…

力扣周赛398题解

特殊数组Ⅰ 如果数组的每一对相邻元素都是两个奇偶性不同的数字&#xff0c;则该数组被认为是一个 特殊数组 。 Aging 有一个整数数组 nums。如果 nums 是一个 特殊数组 &#xff0c;返回 true&#xff0c;否则返回 false。 示例 1&#xff1a; 输入&#xff1a;nums [1] …

数据结构和算法|排序算法系列(二)|冒泡排序

首先需要你对排序算法的评价维度和一个理想排序算法应该是什么样的有一个基本的认知&#xff1a; 《Hello算法之排序算法》 主要内容来自&#xff1a;Hello算法11.3 冒泡排序 我觉得冒泡排序非常有意思&#xff0c;也非常简单&#xff0c;就是不停地交换相邻的元素即可&#…

代码随想录算法训练营第四天| 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点 、 面试题 02.07. 链表相交、142.环形链表II

24. 两两交换链表中的节点 题目链接&#xff1a; 24. 两两交换链表中的节点 文档讲解&#xff1a;代码随想录 状态&#xff1a;没做出来&#xff0c;没有正确更新头节点&#xff0c;因为head和cur共享引用&#xff0c;会随着cur的移动&#xff0c;丢失之前存放的节点 错误代码&…

腾讯发布ELLA:为扩散模型注入LLM能力,提升复杂场景的图像生成,准确率超90%

前言 近年来&#xff0c;基于扩散模型的文本到图像生成技术取得了显著进步&#xff0c;能够生成高质量、逼真的图像。然而&#xff0c;大多数扩散模型仍然使用CLIP作为文本编码器&#xff0c;这限制了它们理解复杂提示的能力&#xff0c;例如包含多个物体、详细属性、复杂关系…

摄像头应用测试

作者简介&#xff1a; 一个平凡而乐于分享的小比特&#xff0c;中南民族大学通信工程专业研究生在读&#xff0c;研究方向无线联邦学习 擅长领域&#xff1a;驱动开发&#xff0c;嵌入式软件开发&#xff0c;BSP开发 作者主页&#xff1a;一个平凡而乐于分享的小比特的个人主页…

MySQL(一) 库和表的基础操作

1. 数据库基础 1.1 什么是数据库 存储数据用文件就可以了&#xff0c;为什么还要弄个数据库? 文件保存数据有以下几个缺点&#xff1a; 文件的安全性问题文件不利于数据查询和管理文件不利于存储海量数据文件在程序中控制不方便 数据库存储介质&#xff1a;磁盘内存 为了解…

学 C/C++ 具体能干什么?

学习 C 和 C 后&#xff0c;你可以从事许多不同的工作和项目&#xff0c;这两种语言以其高性能和低级控制而闻名&#xff0c;特别适合以下几个领域&#xff1a; 1. 系统编程 C 和 C 是系统编程的首选语言&#xff0c;适用于操作系统、驱动程序和嵌入式系统开发。 操作系统开发…

PgMP:项目集管理,哪些人适合学习?

美国项目管理协会&#xff08;PMI&#xff09;对项目集经理&#xff08;Program Manager&#xff09;的角色做出如下的定义&#xff1a; 在最少的领导/监督下&#xff0c;项目集经理PgMP负责在商业和组织目的下协调管理多个相关项目。这些项目含有跨部门、组织、地理区域…