深入理解Java虚拟机:Jvm总结-类文件结构以及类加载机制

news2024/9/20 9:25:58

第六章 类文件结构

6.1 意义

代码编译的结果从本地机器码转变为字节码,冲破了平台界限。

6.2 无关性的基石

实现语言无关性的基础仍然是虚拟机和字节码存储格式。Java虚拟机不与包括Java语言在内的任何程序语言绑定,它只与“Class文件”这种特定的二进制文件格式所关联。

6.3 Class类文件的结构

  • 任何一个Class文件都对应着唯一的一个类或接口的定义信息,但是反过来说,类或接口并不一定都得定义在文件里(譬如类或接口也可以动态生成,直接送入类加载器中)

  • Class文件是一组以8个字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在文件之中,中间没有添加任何分隔符

  • Class文件格式采用一种类似于C语言结构体的伪结构来存储数据,这种伪结构中只有两种数据类型:“无符号数”(以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数)和“表”(多个无符号数或者其他表作为数据项构成的)
    在这里插入图片描述

  • 以上的顺序和数量是固定的

6.3.1 魔数与Class文件的版本

  • 每个Class文件的头4个字节被称为魔数,值为0xCAFEBABE
  • 紧接着魔数的4个字节存储的是Class文件的版本号,第5和第6个字节是次版本号,第7和第8个字节是主版本号

6.3.2 常量池

  • 紧接着主、次版本号之后的是常量池入口,常量池的入口需要放置一项u2类型的数据,代表常量池容量计数值,从1开始。
  • 常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)
  • 读取时查看即可

6.3.3 访问标志

在常量池结束之后,紧接着的2个字节代表访问标志(access_flags),这个标志用于识别一些类或者接口层次的访问信息

第七章 虚拟机类加载机制

7.1 意义

Class文件中的各类信息需要加载到虚拟机上才能被使用,Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程就是虚拟机的类加载机制。Java是先编译后执行的语言,类型的加载、连接和初始化在运行期间执行,虽然类加载时会有一些开销,但是也让Java应用具有极高扩展性和灵活性,这个特性就是依赖于动态连接和动态加载的特点实现的。

7.2 类加载的时机

一个类型的生命周期:
在这里插入图片描述

  • 其中除了解析(某些情况会在初始化之后开始)和使用,其他五个阶段开始的顺序是确定的,但不代表完成一个阶段才会进入下一个阶段,通常都是交叉混合进行的。

  • 类的初始化通常是在第一次主动使用该类的时候才会发生,必须对类初始化的时机,有且只有以下六种情况:

    • 遇到newgetstaticputstaticinvokestatic这四条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化阶段。典型Java代码场景:
      • 使用new实例化对象时
      • 读取或设置一个类的静态字段(被final修饰、编译期就已经放入常量池的除外)
      • 调用一个类的静态方法时
    • 反射调用时
    • 初始化时发现父类没初始化,就要先初始化父类
    • 虚拟机启动会初始化主类
    • 当使用JDK 7新加入的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化。
    • 当一个接口中定义了JDK 8新加入的默认方法(被default关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化
  • 接口的初始化与类差不多,区别只是接口初始化时不需要初始化父接口,当使用父接口时才初始化。

7.3 类加载过程

类加载的过程是一个类生命周期的前五项,也就是,加载、验证、准备、解析、初始化。

7.3.1 加载

  • 加载阶段时,Java虚拟机要干三件事:

    1. 通过一个类的全限定名来获取定义此类的二进制字节流
    2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
    3. 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

    这个阶段灵活性很大,使用者除了可以使用虚拟机内置的类加载器完成,还可以自定义这三件事的具体实现方式

  • 数组类有些不同,是由Java虚拟机直接在内存中动态构造出来的。但是数组包含的元素还是要靠类加载器完成。显然,数组类的可访问性与组件类型的可访问性相同,具体实现过程:

    • 如果数组的组件类型(去掉一个维度)是引用类型,就递归这个加载过程,这个数组将被标识在加载该组件类型的类加载器的类名称空间上
    • 如果数组的组件类型不是引用类型,Java虚拟机将会把这个数组标记为与引导类加载器关联

7.3.2 验证

  • 验证是连接的第一步,是为了确保Class文件的字节流中的信息是安全的。

  • 验证会完成以下四个阶段,后三个阶段直接基于方法区上进行,不会再直接读取、操作字节流:

    1. 文件格式验证:要验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理,也就是保证输入的字节流能正确地解析并存储于方法区之内。
    2. 元数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合《Java语言规范》的要求
    3. 字节码验证:通过数据流分析和控制流分析,确定程序语义是合法的、符合逻辑的。也就是不会做出危害虚拟机安全的行为。但是不能保证绝对的安全,因为存在“停机问题”。JDK6之后增加“StackMapTable”把尽可能多的校验辅助措施挪到Javac编译器里进行。
    4. 符号引用验证:在解析阶段发生,检查该类是否缺少或者被禁止访问它依赖的某些外部类、方法、字段等资源,确保解析行为正常执行。
  • 虽然重要,却不是必须执行,因为一些有使用经验的安全的类,可以省去验证,缩短加载时间。

7.3.3 准备

为静态变量分配内存并设置初始值的过程。注意区别与初始化的赋值是不同的

7.3.4 解析

  • 解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程,首先要明白什么是符号引用和直接引用:

    • 符号引用是指在代码中使用名称(变量名、类名、方法名等符号)来引用某个实体,而不是直接使用内存地址或其他直接引用方式。符号引用主要存在于源代码和编译过程的中间表示中
    • 直接引用是指在代码中直接使用内存地址或其他底层实现方式来引用某个实体。直接引用通常是在运行时使用的,特别是在编译后的代码中
  • 并未规定具体时间,可以根据需求自行决定,类被加载器加载时就对常量池中的符号引用解析,还是等到他被使用前才解析。

  • 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符这7类符号引用进行,分别对应于常量池的CONSTANT_Class_infoCON-STANT_Fieldref_infoCONSTANT_Methodref_infoCONSTANT_InterfaceMethodref_infoCONSTANT_MethodType_infoCONSTANT_MethodHandle_infoCONSTANT_Dyna-mic_infoCONSTANT_InvokeDynamic_info 8种常量类型

    1. 类或接口的解析:假设当前代码所处的类为D,如果要把一个从未解析过的符号引用N解析为一个类或接口C的直接引用,这里主要是为了区分代码所处的类和引用的类,包含三个步骤:

      1. 首先判断C是不是数组类型,如果不是,虚拟机将会把代表N的全限定名传给D的类加载器去加载这个类C,这个过程可能会触发其他类的加载,比如父类或实现的接口
      2. 如果C是数组类型,且元素类型为对象(N的描述符会是类似“[Ljava/lang/Integer”)就会按照第一点的规则加载元素类。
      3. 解析完成前还要进行符号引用验证,确认D是否具备对C的访问权限
    2. 字段解析:前提字段所属的类或接口的符号引用解析完成。四个步骤:

      1. 查找C本身是否包含匹配的字段
      2. 没查到的情况下,如果C实现了接口,就递归查找接口以及父接口中是否包含匹配的字段
      3. 还没有的话,递归查找父类。
      4. 否则,查找失败,抛出java.lang.NoSuchFieldError异常

      查找成功的话,进行权限验证

    3. 类的方法解析:与字段解析类似,前提方法所属的类或接口的符号引用解析完成,五个步骤:

      1. 如果C是接口,直接抛出异常
      2. 如果是类,则在本类中查找
      3. 没有的话,递归在父类中查找
      4. 还没有的话,则在接口和父接口中递归查找,如果匹配到了,说明这是一个抽象类,抛出java.lang.AbstractMethodError异常
      5. 否则,查找失败,抛出异常
    4. 接口方法解析:前提接口所属的类或接口的符号引用解析完成,五个步骤:

      1. 如果发现C是类,直接抛出异常
      2. 如果是接口,在本接口中查找
      3. 没有的话,在父接口递归查找,直到java.lang.Object类(接口方法的查找范围也会包括Object类中的方法)为止。
      4. 由于接口存在多重继承,如果存在多个匹配的,返回其中一个
      5. 否则,查找失败,抛出异常

    7.3.5 初始化

  • 根据程序员的代码来初始化类变量,也就是执行类构造器()方法的过程

  • 这个方法不由程序员编写,他会按照代码编写时的顺序自动收集类中给变量赋值的动作和静态语句块(静态语句块不可以访问定位在他之后的静态变量)。并且自动调用父类的()方法,但不会自动调用父接口的()方法

  • 需要加锁同步,防止多个线程去初始化一个类。

7.4 类加载器

类加载器的作用:通过一个类的全限定名来获取描述该类的二进制字节流

7.4.1 类与类加载器

对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。也就是判断两个类相等时,必须来自同一个类加载器才有可能相等。

7.4.2 双亲委派模型

Java一直保持着三层类加载器、双亲委派的类加载架构:

  • 启动类加载器也叫引导类加载器:使用C++实现,负责加载存放在<JAVA_HOME>\lib目录的类库,无法被Java程序引用。

  • 扩展类加载器:以Java代码的形式实现的。它负责加载<JAVA_HOME>\lib\ext目录中,或者被java.ext.dirs系统变量所指定的路径中所有的类库

  • 应用程序类加载器:由sun.misc.Launcher$AppClassLoader来实现,默认的系统类加载器。

  • 双亲委派模型

  • 在这里插入图片描述

    工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,所以一定会经过顶层类加载器,只有当父类加载器无法加载时,子类才尝试自己加载。集中在java.lang.ClassLoader的loadClass()方法之中

7.4.4 破坏双亲委派模型

破坏的意义在于创新,主要出现过三次:

  • 引入双亲委派模型时,为了兼容已有代码,添加一个新的protected方法findClass(),并引导用户编写的类加载逻辑时尽可能去重写这个方法,因为父类加载失败时,会自动调用自己的findClass()方法来完成加载,所以不会影响。
  • 基础类型又要调用回用户的代码的情况,JDK提供了java.util.ServiceLoader类,以META-INF/services中的配置信息,辅以责任链模式完成父类加载器去请求子类加载器完成类加载的行为
  • 热部署的需求:OSGi, 它自定义的类加载器机制的实现,每一个程序模块(OSGi中称为Bundle)都有一个自己的类加载器,当需要更换一个Bundle时,就把Bundle连同类加载器一起换掉以实现代码的热替换。由树状双亲委派模型变成了网状,出现了平级的类查找。

7.5 Java模块化系统

  • 模块化的目标——可配置的封装隔离机制

  • 解决JDK 9之前基于类路径(ClassPath)来查找依赖的可靠性问题。模块内可以声明对其他模块的显示依赖,这样启动时就可以避免一些类型依赖导致的运行时才会出现的问题

7.5.1 模块的兼容性

提出了模块路径的概念,只关注于存放的路径,简单来说,就是某个类库到底是模块还是传统的JAR包,只取决于它存放在哪种路径上。只要是放在类路径上的JAR文件,无论其中是否包含模块化信息(是否包含了module-info.class文件),它都会被当作传统的JAR包来对待;相应地,只要放在模块路径上的JAR文件,即使没有使用JMOD后缀,甚至说其中并不包含module-info.class文件,它也仍然会被当作一个模块来对待。

7.5.2 模块化下的类加载器

  • 扩展类加载器(Extension Class Loader)被平台类加载器(Platform Class Loader)取代。

  • 平台类加载器和应用程序类加载器都不再派生自java.net.URLClassLoader,启动类加载器、平台类加载器、应用程序类加载器全都继承于jdk.internal.loader.BuiltinClassLoader
    在这里插入图片描述

  • 类加载的委派关系也发生了变动。当平台及应用程序类加载器收到类加载请求,在委派给父加载器加载前,要先判断该类是否能够归属到某一个系统模块中,如果可以找到这样的归属关系,就要优先委派给负责那个模块的加载器完成加载

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

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

相关文章

vue2实践:el-table实现由用户自己添加删除行数的动态表格

需求 项目中需要提供一个动态表单&#xff0c;如图&#xff1a; 当我点击添加时&#xff0c;便添加一行&#xff1b;点击右边的删除时&#xff0c;便删除这一行。 至少要有一行数据&#xff0c;但是没有上限。 思路 这种每一行的数据固定&#xff0c;但是不定行数的&#x…

校园水电费管理|基于java的校园水电费管理小程序系统 (源码+数据库+文档)

校园水电费管理 目录 基于java的校园水电费管理小程序系统 一、前言 二、系统设计 三、系统功能设计 小程序端 后台功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕…

Selenium使用浏览器用户配置进行测试

本文主要介绍了如何在使用Selenium WebDriver进行自动化测试时&#xff0c;创建和使用自定义的Firefox配置文件。 什么是Firefox配置文件&#xff1f; Firefox会将用户的个人信息&#xff0c;如书签、密码和用户偏好设置存储在一个称为配置文件的文件集合中&#xff0c;这些文…

C++设计模式——Iterator迭代器模式

一&#xff0c;迭代器模式的定义 迭代器模式是一种行为型设计模式&#xff0c;它使得遍历一个容器对象中的元素变得更加简单。 迭代器模式将遍历操作从容器对象&#xff08;如集合、列表&#xff09;中分离出来&#xff0c;它通过迭代器对象来遍历容器对象中的元素&#xff0…

若依后端正常启动但是uniapp移动端提示后端接口异常

pc端能用模拟器也能正常连接接口&#xff0c;手机端真机调试连不上接口 解决&#xff1a; 1. 先看config.js的 填自己的ip地址 module.exports { // baseUrl: https://vue.ruoyi.vip/prod-api, baseUrl: "http://192.168.101.5:8080", } 2.网络环境问题&#…

mysql -小计

//表单某字段值为当前打开文档Id (function () { var rdoc getRelateDocument(); var warehouseName rdoc.getItemValueAsString(“warehouseName”); var name rdoc.getItemValueAsString(“name”); var color rdoc.getItemValueAsString(“color”); var batchNumber r…

2024年Web前端JavaScript面试题整理附答案

&#xff08;1&#xff09;两等号判等&#xff0c;会在比较时进行类型转换&#xff1b; &#xff08;2&#xff09;三等号判等(判断严格)&#xff0c;比较时不进行隐式类型转换&#xff0c;(类 型不同则会返回false)&#xff1b; &#xff08;3&#xff09;Object.is 在三等号…

基于风力发电系统的开关磁阻Simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于风力发电系统的开关磁阻Simulink建模与仿真&#xff0c;开关磁阻风力发电系统&#xff08;Switched Reluctance Wind Power Generation System&#xff09;利用开关磁阻电…

计算机的错误计算(八十九)

摘要 探讨反双曲余切函数 acoth(x) 在 附近的计算精度问题。 Acoth(x) 函数的定义为&#xff1a; 其中 x 的绝对值大于 1 . 例1. 计算 acoth(1.000000000002) . 不妨在 Excel 的单元格中计算&#xff0c;则有&#xff1a; 若在Python中用定义直接计算&#xff0c;则有几乎…

SpringCloud神领物流学习笔记:项目概述(一)

SpringCloud神领物流学习笔记&#xff1a;项目概述&#xff08;一&#xff09; 文章目录 SpringCloud神领物流学习笔记&#xff1a;项目概述&#xff08;一&#xff09;1、项目介绍2、基本业务流程3、系统架构4、技术架构 1、项目介绍 ​ 神领物流是一个基于微服务架构体系的【…

Visual Studio 在 .NET MAUI 安装期间无法安装 OpenJDK v8 - 访问被拒绝

优质博文&#xff1a;IT-BLOG-CN 问题 我一直在 Windows 计算机上设置 Visual Studio 以进行 .NET MAUI 开发&#xff0c;但在设置过程中一直遇到问题。具体问题涉及 OpenJDK v8 无法安装。这是我看到的情况&#xff1a; Couldnt install OpenJDKv8我尝试过几种方法来解决这…

金融工程--论文资料整理方法

背景 1、金融工程的主要参考文献来源&#xff0c;帮助了解最新的量化研究进展。 2、这个工作其实对于所有的工程领域都能够使用。 3、如何使用ai工具提升阅读效率&#xff1f; 期刊文献来源 文献来源 https://zhuanlan.zhihu.com/p/549070754 这个网站能够很好帮助找到相关…

Linux(Ubuntu和CentOS通用)系统下源码安装Nginx_ubuntu和centos软件通用吗

<1>进入/home/zhangbao/nginx_install目录&#xff1a;cd /home/zhangbao/nginx_install <2>解压openssl-fips-2.0.16.tar.gz文件&#xff1a;tar -zxvf openssl-fips-2.0.16.tar.gz <3>进入openssl-fips-2.0.16解压包中&#xff1a;cd openssl-fips-2.0.16 …

什么是Aware注入?

Spring容器可以在Bean初始化的时候&#xff0c;自动注入一些特定信息&#xff08;如beanfactory&#xff09;,使得bean可以轻松的访问其他Bean的实例&#xff0c;简化代码&#xff0c;避免了显式的注入。 Spring提供了很多Aware的接口,如下&#xff1a; 拿其中的BeanFactoryAwa…

SprinBoot+Vue健身俱乐部网站系统的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优质…

编曲术语:各种段落的英文表示 Cubasis和Cubase联合编曲

在编曲中&#xff0c;常见的段落英文表示如下&#xff1a; 前奏&#xff08;Intro&#xff09;&#xff1a;通常是歌曲开头的部分&#xff0c;用于引入主题&#xff0c;营造氛围。 主歌&#xff08;Verse&#xff09;&#xff1a;歌曲的主要叙述部分&#xff0c;一般有多段&am…

Vue实现自定义进度条占比功能 Vue自定义进度条功能

在不使用echarts等第三方插件的情况下,使用Vue实现自定义的进度条占比功能,并且是多数据可选循环的 预览图效果 首先看一下获取的后端返回的数据结构,其中每一组加起来等于 22 ,也就是说如果你自己算的话也是同理,根据 占比的数值 除以 总和 =的百分比再去渲染对应占比…

828华为云征文 | 标题:使用华为云X实例部署图数据库Virtuoso并存储6500万条大数据的完整过程与性能测评

前言 在大数据时代&#xff0c;图数据库以其强大的关系处理能力在复杂网络、社交媒体分析、知识图谱等领域得到了广泛应用。而在云计算的蓬勃发展下&#xff0c;使用云服务器进行图数据库的部署与管理变得更加方便高效。本篇文章将详细介绍如何在华为云X实例上部署开源图数据…

C++(2)之Linux多线程服务端编程总结

C之Linux多线程服务端编程读书笔记 Author: Once Day Date: 2023年1月31日/2024年8月23日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: Linux实践…

MySQL-CRUD入门2

文章目录 数据的查询(补充)条件查询关于SQL语句的执行顺序分页查询(LIMIT) 数据的修改数据修改基础知识 数据的查询(补充) 这一节接着写, 包括数据的查询(补充), 数据的更新, 数据的删除 条件查询 其实就是根据给定的一些条件, 然后过滤掉不符合实际情况的记录, 把符合条件的…