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

【JVM原理】类加载机制

文章目录

  • 一、JVM组成
  • 二、类的生命周期
    • 2-1 加载 (Loading)
    • 2-2 连接 (Linking)
    • 2-3 初始化 (Initialization)
    • 2-4 使用 (Using)
    • 2-5 卸载 (Unloading)
  • 三、类加载器
    • 3-1 类加载器的作用
    • 3-2 类加载器的种类
    • 3-3 类加载机制
      • 双亲委派机制(Parent Delegation Model)
      • 全盘负责委托机制(Full Responsibility Delegation Model)

JVM (Java Virtual Machine)


一、JVM组成

JVM包括几个核心组件:

类加载器子系统:负责加载、链接和初始化类。是实现类加载机制的具体实现部分,它包括各种类加载器和它们的行为。

运行时数据区:包括堆、方法区、Java 栈、程序计数器和本地方法栈等,用于存储类、对象、方法和线程的执行状态。

执行引擎:执行字节码,包括解释器和即时编译器(JIT)。

本地接口:与本地方法库(如 C/C++)进行交互。

垃圾回收器:自动管理内存,回收不再使用的对象。

我们写的程序经过编译后成为了.class文件,.class文件中描述了类的各种信息,最终都需要加载到虚拟机之后才能运行和使用。而虚拟机如何加载这些.class文件?.class文件的信息进入到虚拟机后会发生什么变化?

二、类的生命周期

类的整个生命周期包括:加载验证准备解析初始化使用卸载这7个阶段。

在这里插入图片描述

2-1 加载 (Loading)

类加载器: 当 JVM 需要一个类时,它会通过类加载器来加载这个类。类加载器负责从文件系统、网络等地方加载类的字节码。(类的唯一性 全限定名)

类文件: 类加载器从类路径(通常是 .jar 文件或 .class 文件)中读取字节码。

转换为 Class 对象: 读取的字节码被转换为 java.lang.Class 对象,表示这个类的结构和行为。Class 对象会被存放在 JVM 的方法区(Metaspace)。

2-2 连接 (Linking)

连接阶段包括三个子阶段:

验证 (Verification): 验证字节码的合法性和安全性,确保字节码符合 Java 虚拟机规范,不会破坏 JVM 的安全性。

准备 (Preparation): 为类的静态变量分配内存,并将它们初始化为默认值(如 null0false 等)。这些变量所使用的内存都将在方法区中分配。 这一步并不包括初始化静态变量的实际值。

public static int num = 10;
public static final int num = 10;
//准备阶段赋初始值的变量指的是那些不被final修饰的static变量

解析 (Resolution): 将类中的符号引用转换为直接引用。符号引用是在编译期生成的,用于描述类、方法或字段的引用。解析过程将这些符号引用解析为实际的内存地址或引用。

2-3 初始化 (Initialization)

直到初始化阶段, Java虚拟机才真正开始执行类中编写的Java程序代码, 将主导权移交给应用程序。

  • 静态初始化块: 在这个阶段,类的静态变量会被初始化为显式的值,同时会执行类的静态初始化块(如果存在)。
  • 执行静态块: 类的静态初始化块按照在源代码中出现的顺序执行。

这一过程由编译器自动生成静态初始化方法(<clinit>()),当 Java 类被加载时,JVM 会调用 <clinit>() 方法以初始化类的静态成员。这包括静态变量的赋值和静态初始化块中的代码。

class Test {static class Parent {public static int A = 1;static {A = 2;}} static class Sub extends Parent {public static int B = A;}public static void main(String[] args) {System.out.println(Sub.B);}
}
// Java虚拟机会保证在子类的()方法执行前,父类的()方法已经执行完毕。因此在Java虚拟机中第一个被执行的()方法的类型肯定是java.lang.Object

2-4 使用 (Using)

  • 实例化: 当程序需要一个类的实例时,JVM 会使用加载好的 Class 对象来创建对象。
  • 方法调用: 在程序运行过程中,通过 Class 对象调用该类的方法。

2-5 卸载 (Unloading)

  • 垃圾回收: 如果一个类的 ClassLoader 对象被垃圾回收器回收,那么该类也可能被卸载。卸载是为了释放内存,并且需要满足一定条件(例如,类的 ClassLoader 已被回收,类不再有活跃的引用)。

全限定名(Fully Qualified Name)是确定类唯一性的一种重要方法,但它并不是唯一的方法。全限定名包括包名和类名(例如 com.example.MyClass),可以唯一标识一个类。不过,如果有多个类在不同的类加载器中存在相同的全限定名,它们也可能被认为是不同的类。因此,类的唯一性还依赖于类加载器的上下文和类加载机制。

三、类加载器

3-1 类加载器的作用

层次结构管理:Java 中的类加载器以层次结构组织,确保每个类加载器都能找到其需要的类,并且保证不同加载器加载的同名类是隔离的。

3-2 类加载器的种类

jvm支持两种类型的加载器,即启动类加载器和其他类加载器,其中,启动类加载器是由c/c++实现的,其他类加载器是由java实现的。

上图中的加载器划分为包含关系而并非继承关系

启动类加载器(Bootstrap ClassLoader):加载核心 Java 类库,如 rt.jar 中的类。它是最基本的类加载器,使用c/c++实现,嵌套在jvm内部。

扩展类加载器(Platform ClassLoader/Extension ClassLoader):加载 Java 平台扩展目录中的类,通常是 jre/lib/ext 目录下的类。由 sun.misc.Launcher$ExtClassLoader 实现。

系统类加载器(Application ClassLoader):加载应用程序的类路径(classpath)中的类,是用户定义的类加载器的默认实现。由 sun.misc.Lanucher$AppClassLoader 实现。

自定义类加载器(Custom ClassLoader):开发者可以自定义实现,继承 ClassLoader 类,加载特定的类文件,满足特殊需求。例如,加载网络资源中的类文件或实现热部署功能。

在这里插入图片描述

3-3 类加载机制

双亲委派机制(Parent Delegation Model)

双亲委派机制是 Java 类加载器的主要工作模式,保证了 Java 程序的安全性和一致性。其工作流程如下:

  1. 委派
  2. 递归委派
  3. 父加载器的尝试
  4. 加载失败

如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时( ClassNotFoundException ),子加载器才会尝试自己去加载。

双亲委派机制的优点

提高安全性:确保了核心类库(如 java.lang.*java.util.*)不会被用户自定义的类加载器所覆盖。所有的类加载请求都会先委派给父类加载器,如果父类加载器无法找到该类,才会由当前类加载器尝试加载。这种机制可以防止恶意代码通过自定义类加载器覆盖 Java 核心类。

减少类的重复加载:避免重复加载相同的类。减少内存中的类实例数目,并避免不同类加载器加载同一类的冲突。

在这里插入图片描述

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}

从上面代码可以明显看出, loadClass(String, boolean) 函数即实现了双亲委派模型。整个大致过程如下:

首先,检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。

如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加载器加载(即调用 parent.loadClass(name, false); ).或者是调用 bootstrap 类加载器来加载。

如果父加载器及 bootstrap 类加载器都没有找到指定的类,那么调用当前类加载器的 findClass 方法来完成类加载。

全盘负责委托机制(Full Responsibility Delegation Model)

全盘负责委托机制(或称为"全权负责"模式)是指某个类加载器在接收到加载类的请求时,不将请求委派给父加载器,而是由该加载器自己完全负责加载过程。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 《比较教育研究》
  • Qt Creator 配置pcl1.14.1
  • 电脑屏幕怎么添加水印
  • Qt 学习第7天:Qt核心特性
  • Vue——认识day04_计算属性(案例:实时预览)
  • 望繁信科技亮相2024数博会:以流程智能引领数字化转型新未来
  • KTH5701 系列低功耗、高精度 3D 霍尔传感器
  • 【蓝牙协议栈】【BLE】【GATT】精讲GATT Profile架构(图文并茂精华版)
  • Cesium 和 three.js 对数深度缓冲原理简析
  • 【58同城-注册安全分析报告】
  • 计算机网络 TCPUDP、IP、ARPRARP、NAT总结
  • 源代码加密软件有哪些?11款超好用的源代码加密软件推荐
  • chapter08-面向对象编程——(Object类详解)——day09
  • ubuntu c++ http服务端event使用
  • AI学习记录 - 模型训练中怎么反向传播以及学习率的影响
  • JS中 map, filter, some, every, forEach, for in, for of 用法总结
  • 【css3】浏览器内核及其兼容性
  • 【笔记】你不知道的JS读书笔记——Promise
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • AngularJS指令开发(1)——参数详解
  • Apache Spark Streaming 使用实例
  • Apache Zeppelin在Apache Trafodion上的可视化
  • HashMap剖析之内部结构
  • JavaScript 基本功--面试宝典
  • Mysql优化
  • passportjs 源码分析
  • pdf文件如何在线转换为jpg图片
  • SpriteKit 技巧之添加背景图片
  • Vue 重置组件到初始状态
  • 仿天猫超市收藏抛物线动画工具库
  • 复习Javascript专题(四):js中的深浅拷贝
  • 构建二叉树进行数值数组的去重及优化
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 坑!为什么View.startAnimation不起作用?
  • 离散点最小(凸)包围边界查找
  • 目录与文件属性:编写ls
  • 前端
  • 前端技术周刊 2018-12-10:前端自动化测试
  • 浅谈Golang中select的用法
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • 使用docker-compose进行多节点部署
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 通过几道题目学习二叉搜索树
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • Oracle Portal 11g Diagnostics using Remote Diagnostic Agent (RDA) [ID 1059805.
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • ‌移动管家手机智能控制汽车系统
  • # linux 中使用 visudo 命令,怎么保存退出?
  • #php的pecl工具#
  • (1)(1.19) TeraRanger One/EVO测距仪
  • (4)logging(日志模块)
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十