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

JVM 虚拟机的编译器、类加载过程、类加载器有哪些?

JVM 虚拟机的编译器

编译器可以分为:前端编译器、JIT 编译器、AOT编译器

img

前端编译器:源代码 --> 字节码

在Java语言中,JDK安装目录中的javac就是编译器。它负责将Java源代码编译为字节码。因为处于编译的前期,javac也叫做前端编译器。

img

JIT 编译器:字节码 --> 机器码

即时编译器(Just-In-Time Compiler,JIT)

Java源代码转化为 字节码之后,要运行它,有两种选择:

  1. 使用 Java 解释器,执行字节码。
  2. 使用 JIT(Just-In-Time Compiler) 编译器 将字节码 转化为 机器码。

img

JIT 编译器在程序运行时,对频繁执行的字节码进行即时编译,将其转换为本地机器码,从而提高程序的执行效率

HotSpot 虚拟机的两个即时编译器

在 HotSpot 虚拟机内置了两个即时编译器,分别称为 Client CompilerServer Compiler。这两种不同的编译器衍生出两种不同的编译模式,我们分别称之为:C1 编译模式C2 编译模式

  • C1 编译模式会将字节码编译为本地代码,进行简单、可靠的优化,如有必要将加入性能监控的逻辑。
  • C2 编译模式,也是将字节码编译为本地代码,但是会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。

AOT 编译器:源代码 --> 机器码

提前编译器(Ahead-of-Time Compiler,AOT)

AOT 编译器的基本思想是:在程序执行前生成 Java 方法的本地代码,以便在程序运行时直接使用本地代码

HotSpot 虚拟机的三种运行模式

注意,对于 HotSpot 虚拟机来说,其一共有三种运行模式可选,分别是:

  • 混合模式(Mixed Mode) 。即 C1 和 C2 两种模式混合起来使用,这是默认的运行模式。如果你想单独使用 C1 模式或 C2 模式,使用 -client-server 打开即可。
  • 解释模式(Interpreted Mode)。即所有代码都解释执行,使用 -Xint 参数可以打开这个模式。
  • 编译模式(Compiled Mode)。 此模式优先采用编译,但是无法编译时也会解释执行,使用 -Xcomp 打开这种模式。

在命令行中输入 java -version 可以看到,我机器上的虚拟机使用 Mixed Mode 运行模式。

image.png

JVM 虚拟机类加载过程

Java 中的类加载过程分为:加载(Loading)链接(Linking)初始化(Initialization)
在这里插入图片描述

  1. 加载(Loading):阶段的目的是将类的.class文件加载到JVM中。在这个阶段,JVM 会根据类的全限定名来获取定义该类的二进制字节流,并将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构

  2. 链接(Linking):又分为 验证(Verification)准备(Prepartion)解析(Resolution)

    1. 验证:校验类的正确性,包括:
      1. 文件格式验证:验证字节流是否符合Class文件格式的规范。比如,是否以0xCAFEBABE开头。
      2. 元数据验证:对字节码描述进行语义分析,是否遵循Java规范。比如一个类是否继承了多个类。
      3. 字节码验证:比如验证指令代码序列是否能够正常工作。
      4. 符号引用验证:确保解析动作能够正确执行。
    2. 准备:给 类中定义的变量(被static修饰的静态变量)分配内存,并设置初始值。
    3. 解析:虚拟机将 常量池 内的 符号引用 替换为 直接引用。(解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符 7 类符号引用进行)
  3. 初始化(Initialization):初始化阶段是执行初始化方法 <clinit>()方法的过程,是类加载的最后一步,这一步 JVM 才开始真正执行类中定义的 Java 程序代码(字节码)。

    对于初始化阶段,有些情况必须立即初始化(如下)。其他情况,则是进行懒加载(首次用到的时候才初始化)

    1. 遇到newgetstaticputstaticinvokestatic这四条字节码指令时,会立即初始化。
    2. 使用反射机制时,立即初始化。
    3. 初始化一个类时,其父类没初始化,则会立即初始化父类。
  4. 卸载(Unloading):对象被垃圾回收。

类加载器

类加载器 主要作用于 JVM 虚拟机类加载过程中的 加载阶段。

如何比较两个类是否“相等”

对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性。所以:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个 Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等

双亲委派机制

站在JAVA虚拟机的角度来讲,只存在两种不同的类加载器:

  1. 一种是启动类加载器(Bootstrap ClassLoader,这个类加载器使用C++语言实现,是虚拟机自身的一部分
  2. 另外一种就是其他所有的类加载器,这些类加载器都由Java语言实现,独立存在于虚拟机外部,并且全都继承自抽象类 java.lang.ClassLoader

每一个类都有一个对应它的类加载器。系统中的 ClassLoader 在协同工作的时候会默认使用 双亲委派模型。其工作过程为:

  • 当一个类加载器收到了类加载的请求,不会自己去加载这个类,而是委派给父类加载器完成。
  • 只有当父类加载器无法完成这个加载请求时(它搜索范围中没找到对应的类信息),子加载器才会尝试自己去完成加载。

在这里插入图片描述

父类加载器

双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器

三层类加载器

双亲委派机制中的主要的三层类加载器,他们分别是:启动类加载器、扩展类加载器、应用程序类加载器

  • BootstrapClassLoader(启动类加载器) :最顶层的加载类,由 C++实现,负责加载 %JAVA_HOME%/lib目录下的 jar 包和类,或者被 -Xbootclasspath参数指定的路径中的所有类。
  • ExtensionClassLoader(扩展类加载器) :主要负责加载 %JRE_HOME%/lib/ext 目录下的 jar 包和类,或被 java.ext.dirs 系统变量所指定的路径下的 jar 包。
  • AppClassLoader(应用程序类加载器) :面向我们用户的加载器,负责加载当前应用 classpath 下的所有 jar 包和类。

双亲委派机制的好处

双亲委派模型保证了 Java 程序的稳定运行,可以避免类的重复加载(JVM 区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类)。

如何破坏双亲委派机制

答:有如下几种方法:

  1. 通过自定义类加载器,重写loadClass()方法,可以改变这种默认的加载顺序,从而破坏双亲委派机制。

    比如,可以先尝试从自己指定的路径去加载类,如果找不到再调用父类的loadClass方法。

  2. 线程上下文类加载器:当 JVM 需要加载类且当前类加载器无法完成时,会尝试使用线程上下文类加载器,从而绕过双亲委派机制从特定类加载器加载类。

线程上下文类加载器

在双亲委托模型下,类的加载是由下至上的,即下层的类加载器会委托上层进行加载。

但对于SPI来说,有些接口是Java核心库所提供的,而Java核心库是由启动类加载器来加载的,而这些接口的实现却来自不同的Jar包(厂商提供JDBC的实现有Oracle,MySQL等), Java的启动类加载器不会加载其他来源的jar包,这样传统的双亲委托模型就无法满足SPI的要求而通过给当前线程设置上下文类加载器,就可以由设置的上下文类加载器来实现对于接口实现类的加载

SPI(Service Provider Interface)即服务提供者接口

  • SPI 是一种用于实现框架扩展和插件化的机制。它定义了一组接口,允许第三方为这些接口提供具体的实现。
  • 例如,Java 的数据库连接(JDBC)就是一个典型的 SPI 应用。JDBC 定义了一组用于与数据库进行交互的接口,而不同的数据库厂商提供了各自的实现。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 信息技术的革新与未来展望
  • 面试金典题2.6
  • TLV解码 - 华为OD统一考试(E卷)
  • C++第十二节课 模板初阶和string引入
  • 新能源汽车知识点集萃
  • Invalid Object: LngLat(NaN, NaN)高德地图报错
  • 【深入理解SpringCloud微服务】了解微服务的熔断、限流、降级,手写实现一个微服务熔断限流器
  • OpenHarmony(鸿蒙南向开发)——小型系统内核(LiteOS-A)【LMS调测】
  • 十三 系统架构设计(考点篇)
  • 骨传导耳机哪个品牌好用?五大高销骨传导耳机真实测评
  • (总结)(2)编译ORB_SLAM2遇到的错误
  • javaScript第一天学习
  • (done) 声音信号处理基础知识(2) (重点知识:pitch)(Sound Waveforms)
  • c++primer 第八章函数编程答案
  • Python知识点:如何使用Python进行智能合约开发(Solidity、Web3.py)
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • 0x05 Python数据分析,Anaconda八斩刀
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • Django 博客开发教程 16 - 统计文章阅读量
  • iBatis和MyBatis在使用ResultMap对应关系时的区别
  • JS字符串转数字方法总结
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • magento2项目上线注意事项
  • Next.js之基础概念(二)
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • PermissionScope Swift4 兼容问题
  • Unix命令
  • vue:响应原理
  • 阿里云爬虫风险管理产品商业化,为云端流量保驾护航
  • 将 Measurements 和 Units 应用到物理学
  • 警报:线上事故之CountDownLatch的威力
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • 以太坊客户端Geth命令参数详解
  • 鱼骨图 - 如何绘制?
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • 数据可视化之下发图实践
  • ​力扣解法汇总946-验证栈序列
  • ​虚拟化系列介绍(十)
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • ( 10 )MySQL中的外键
  • (19)夹钳(用于送货)
  • (4)STL算法之比较
  • (6)添加vue-cookie
  • (7) cmake 编译C++程序(二)
  • (八)Spring源码解析:Spring MVC
  • (全注解开发)学习Spring-MVC的第三天
  • (十六)一篇文章学会Java的常用API
  • (最简单,详细,直接上手)uniapp/vue中英文多语言切换
  • ../depcomp: line 571: exec: g++: not found
  • .NET C# 使用GDAL读取FileGDB要素类
  • .NET Core引入性能分析引导优化
  • .Net Redis的秒杀Dome和异步执行
  • .Net--CLS,CTS,CLI,BCL,FCL
  • .sh 的运行
  • @ConfigurationProperties注解对数据的自动封装