《深入理解JAVA虚拟机(第2版)》- 第7章 - 学习笔记

news2024/9/20 18:34:14

第7章 虚拟机类加载机制

7.1 概述

  1. 类的加载机制是:虚拟机将描述类的数据从Class文件中读取到内存中,再对这些数据进行检验、转换解析、初始化,最终生成一个可以被虚拟机直接使用的数据类型。
  2. 之所以称Java是动态加载和动态连接,是因为Java的加载、解析、初始化都是在运行期完成的。从简单的Applet、Jsp到复杂的OSGI,都是使用了动态加载这个特性。

7.2 类的加载时机

  1. 类的生命周期是:加载、验证、准备、解析、初始化、使用、卸载。其中验证、准备、解析这三个阶段统称为连接。

  2. 加载、验证、准备、初始化、卸载这五个阶段的顺序是确定的,类的加载必须按着这种顺序按部就班的开始,只有解析阶段不一定,它可以在初始化之后再开始,这是为了支持Java语言的运行时绑定(也称为动态绑定或动态绑定)。

  3. Java虚拟机规范中规定了有且只有5种情况必须立即对类进行“初始化”(在此之前加载、验证、准备必须已经完成),这5种情况中的行为称为对类的主动引用

    这5种情况分别是,如下:

    1. 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
    2. 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
    3. 当一个类被初始化的时候,它的父类都要已经初始化完成了。
    4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
    5. 当使用JDK 1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
  4. 除了主动引用中的那5种情况以外,其他所有引用类的方式都无法触发初始化,称为被动引用

    以下举出几个例子来说明什么是被动引用:

    1. 对于静态字段,只有直接定义了这个字段的类才会被初始化,通过子类来引用父类定义的静态字段,只会触发父类的初始化而不是子类的初始化。
    2. 我们定义一个类的数组,例如:User[] users = new User[10],此时并不会触发User类的初始化,虚拟机会自动生成一个User对应的数组类,该类会被初始化。
    3. 常量字段(被final修饰的字段)会在编辑阶段存入调用类的常量池中,本质上被没有引用定义该常量的类,因此也就不会触这个类的初始化。

与类不同,接口初始化的时候并不要求它的父接口都要先被初始化,只有真正直接用到了父接口的时候(例如:引用父接口定义的常量),才会触发父接口的初始化。

7.3 类的加载过程

7.3.1 加载
  1. 加载阶段要完成以下3件事情:
    1. 根据类的全限定名找到对应的二进制字节流。
    2. 将二进制字节流中的静态存储结构转换为方法区的运行时数据结构。
    3. 在内存生成一个该类对应的java.lang.Class对象,作为方法区该类的引用。
  2. 数组不是由类加载器创建的,而是又虚拟机创建的,作为数组元素的类还是通过类加载器来创建的。
  3. 用来代表类的java.lang.Class对象并没有明确固定存放在堆中,对HotSpot虚拟机来说它比较特殊,它虽然是对象,但是是存放在方法区中的
7.3.2 验证
  1. 验证阶段是为了确保Class文件的二进制字节流包含的信息符合当前虚拟机的要求,不会对虚拟机的运行造成安全隐患。之所以要有该阶段,是因为Class文件不一定都是Java代码生成的。
  2. 验证阶段大致分为文件格式验证、元数据验证、字节码验证、符号引用验证这4个验证。
    1. 文件格式验证

      • 验证二进制字节流是否符合Class文件格式的要求,是否可以被当前版本的虚拟机处理。
      • 注意文件格式验证是针对的二进制字节流,这个验证通过后,二进制字节流会被存放到方法区中,后边的元数据验证、字节码验证、符号引用验证均是对方法区中的数据结构进行操作,而不再是二进制字节流了。
    2. 元数据验证

      对字节码描述的信息进行语义分析,确保其符合Java语言规范。可以理解为对数据类型进行验证。

    3. 字节码验证

      通过对数据流和控制流进行分析,确保程序语义合法、符合逻辑。可以理解为对方法体进行验证。

    4. 符号引用验证

      • 用来确保解析阶段(即符号引用转换为直接引用)能够顺利执行。
      • 符号引用验证发生在将符号引用转换为直接引用的时候,即连接中的解析阶段。
7.3.3 准备
  1. 准备阶段是为类变量分配内存并设置初始化值,这些变量所使用的内存都是在方法区分配的。
  2. 注意准备阶段只为类变量分配内存和设置初始化值,不包括实例变量
  3. 准备阶段设置的初始化值通常情况下是类型的零值,例如:​​public static int value = 123,value在准备阶段的初始化值是0而不是123。相对于通常情况,如果value被final关键字修饰后,例如:public static final int value = 123,此时value在准备阶段的初始值就为123。
7.3.4 解析
  1. 解析阶段是将常量池中的符号引用转换为直接引用的过程。
  2. 符号引用:
    • 符号引用就是用一组符号来描述所引用的目标。
    • 符号引用与虚拟机实现的内存布局无关。
    • 符号引用的目标不一定已经加载到内存当中了。
  3. 直接引用
    • 直接引用是可以直接指向目标的指针、相对偏移量或者能够间接定位到目标的句柄。
    • 直接引用与虚拟机实现的内存布局有很大关系,所以相同符号引用在不同的虚拟机中对应的直接引用也不同。
    • 直接引用的目标肯定已经加载到内存当中了。
7.3.5 初始化
  1. 初始化阶段是执行类构造器()方法的过程。

  2. ()方法是由编译器自动收集类变量赋值语句和static语句块中的语句而生成的。

  3. 在static语句块中,可以给它之后的定义的变量赋值,但是不能访问,如下:

      ​​public class Test {
          static {
              i = 0;   // 给变量赋值可以正常编译通过
             System.out.print(i); // 这句编译器会提示"非法向前引用"
         }
        static int i = 1;
      }
    
  4. 类构造器的<client>()方法与实例构造器的<init>()方法不同,子类可以不需要显示的调用父类构造器的<client>()方法,虚拟机会保证在调用子类的<client>()方法之前先调用父类的<client>()方法。

  5. 由于父类的<client>()方法要先于子类的<client>()方法执行,也就是父类的静态语句块要优先与子类的类变量赋值操作,示例代码如下:

    ​​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);  // 这里输出的是2而不是1
    }​​
    
  6. 类构造器的<client>()方法不是必须的,如果一个类或接口,没有static语句块,也没有静态变量的赋值语句,那么编译器就可以不为这个类生成<client>()方法。

  7. 对于接口来说,虽然没有静态语句块,但是可以在接口中定义静态变量,所以接口一样会有<client>()方法,但是与类的<client>()方法不同,父接口的<client>()方法没有必要在子接口的<client>()方法之前执行

    只有真正使用到接口的时候,例如:引用接口定义的类变量,接口才会被初始化

    类初始化的时候也不会触发执行所实现接口的<client>()方法

7.4 类加载器

  1. 确定类在虚拟机中的唯一性,需要两个要素:类本身和加载它的类加载器。每一个没加载器,都有一个独立的类命名空间。
  2. 绝大多数Java程序会用到下边3中类加载器:
    1. 启动类加载器:JVM的一部分,采用C++实现,用来加载JAVA_HOME/lib下的类库,例如:rt.jar
    2. 扩展类加载器:用来加载JAVA_HOME/lib/ext下的类库。开发者可以直接使用该类加载器
    3. 应用程序类加载器:也称为系统类加载器,用来加载用户路径(CLASS_PATH)下的类库,如果应用程序没有自定义类加载器,一般情况下这个应用程序类加载就作为默认的类加载器。
  3. 双亲委派模型
    类加载器的双亲委派模型如下:
    在这里插入图片描述
    1. 双亲委派模型是用来描述类加载器之间的层次关系。
    2. 双亲委派模型的工作过程是:当一个类加载器收到某类的加载请求的时候,会将该请求委派给它的父类加载器,逐层委派,直到启动类加载器,只有当父类加载器无法加载的时候,子类加载器才会自己尝试加载。
    3. 打破双亲委派模型的几种情况:
      • OSGI:每个Bundle都有一个自己的类加载器,替换的时候整个Bundle都替换包括类加载器
      • SPI(Service Provider Interface),例如:JNDI、JDBC…。拿JNDI服务来举例,JNDI服务可以通过一个叫做线程上下文类加载器的东西来加载所需的SPI代码,也就是父类加载器通过调用子类加载器来完成类加载动作

上一篇:《深入理解JAVA虚拟机(第2版)》- 第6章 - 学习笔记
下一篇:《深入理解JAVA虚拟机(第2版)》- 第8章 - 学习笔记

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

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

相关文章

如何在Windows系统上安装和配置Git客户端

&#x1f600;前言 在这篇博文中&#xff0c;我将为大家介绍如何在Windows系统上安装和配置Git客户端&#xff0c;包括Git for Windows和TortoiseGit。这些工具是进行版本控制管理的必备工具&#xff0c;特别适合开发者在日常工作中使用。通过这篇文章&#xff0c;您将学会如何…

开放式耳机的优缺点?音质好吗?分享四款开放式蓝牙耳机

作为蓝牙耳机的重度使用患者&#xff0c;我认为开放式耳机音质的好坏取决的因素有好多&#xff0c;包括不同的品牌、不同的‌型号及不同的产品所采用的声学技术&#xff0c;而且开放式耳机的音质好不好也是取决使用者的需求的&#xff0c;有些开放式耳机能提供更清晰的人声音频…

为量产而设计:自动驾驶车辆激光雷达旋转外参在线标定与异常排除策略

更多精彩内容&#xff0c;请关注公众号&#xff1a;智驾机器人技术前线 1.论文信息 论文标题&#xff1a;Fault Detection and Exclusion for Robust Online Calibration of Vehicle to LiDAR Rotation Parameter 作者&#xff1a;Jiwon Seok, Chansoo Kim, Paulo Resende, B…

黑马点评4——优惠券秒杀—分布式锁

文章目录 分布式锁什么是分布式锁分布式锁的实现基于redis实现分布式锁分布式锁初级版本redis分布式锁的误删问题分布式锁的原子性问题lua脚本解决多条命令原子性问题Java调用lua脚本改进redis的分布式锁分布式锁-Redisson功能介绍分布式锁-Redisson快速入门Redisson可重入原理…

vue3+vant4父组件点击提交并校验子组件form表单

先看效果 代码如下 父组件 <template><ChildForm ref"childFormRef" /><button click"validateForm">校验表单</button> </template> <script setup> import { ref } from vue; import ChildForm from ./ChildForm.…

【Python系列】异步任务的终止

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

如何在S7-200 SMART CPU断电后保持高速计数器的当前值

1、首先是问题描述 S7-200 SMART的断电数据保持不支持高速计数器的范围设置&#xff0c;因此高速计数器的当前值在CPU每次断电后都会复位为数值0。要使S7-200 SMART高速计数器的当前值在CPU断电重启后依然保持&#xff0c;可以通过以下的编程来实现。 在此以通过向导生成高速…

MapStruct-Java实体转换利器

MapStruct-Java实体转换利器 MapStruct 概述MapStruct 使用例子UserDTOUserEntityUserDTOConverter ( mapstruct 接口)UserModelConverterImpl &#xff08;自动生成&#xff09;Service 层使用 MapStruct 概述 MapStruct 是一个用于生成映射器接口的代码生成库&#xff0c;主…

哪款宠物空气净化器性价比高,希喂、霍尼韦尔真实测评分享

几年前养宠物就帮我开启了一个全新的体验。下班回去后能看到它在门口处摇着尾巴&#xff0c;眨巴着个大眼的等我回家&#xff1b;每天醒来就看到它在我的身边安安静静的躺着&#xff0c;实在是一幅岁月静好的画面。但如果没有浮毛和异味困扰的话&#xff0c;我相信会更好。 随…

ChatTTS文本转语音本地Windows环境部署与远程生成AI音频实战流程

文章目录 前言1. 下载运行ChatTTS模型2. 安装Cpolar工具3. 实现公网访问4. 配置ChatTTS固定公网地址 前言 本篇文章主要介绍如何快速地在Windows系统电脑中本地部署ChatTTS开源文本转语音项目&#xff0c;并且我们还可以结合Cpolar内网穿透工具创建公网地址&#xff0c;随时随…

【BLE】四.SMP安全配对详解

设备配对流程 SMP专业术语 Paring&#xff08;配对&#xff09;&#xff1a; 配对能力交换&#xff0c;设备认证&#xff0c;密钥生成&#xff0c;连接加密以及机密信息分发等 过程 Bonding&#xff08;绑定&#xff09; 配对中会生成一个长期密钥&#xff08;LTK&#xff0c;…

022集—— 字符串按ascii码转数字——C#学习笔记

字符串分解为字符&#xff0c;字符分别转为ascii对应数字&#xff0c;最后数字连接起来转为字符串&#xff0c;具体如下&#xff1a; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace Console…

【Next】3. 开发规范

笔记来源&#xff1a;编程导航 1、约定式路由 Next.js 使用 约定式路由&#xff0c;根据文件夹的结构和名称&#xff0c;自动将对应的 URL 地址映射到页面文件。 常见的几种路由规则如下&#xff1a; 1&#xff09;基础规则&#xff1a;以 app 目录作为根路径&#xff0c;根…

mybatis-plus添加replace(自定义)方法,添加sql注入器SqlInjector

1. 继承DefaultSqlInjector import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.springframework.stereotype.Compon…

网络安全宣传周知识竞赛答题小程序怎么做?

随着网络安全宣传周的到来&#xff0c;越来越多的单位开始组织员工进行线上学习网络安全相关知识&#xff0c;其中举办网络安全知识竞赛答题活动的效果尤为明显&#xff0c;以下是一些网络安全答题系统的功能亮点&#xff1a; 多样化的题目类型与灵活的出题方式&#xff1a; 丰…

92.游戏的启动与多开-多开检测概述

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;易道云信息技术研究院 上一个内容&#xff1a;91.游戏的启动与多开-游戏启动 正常来说任何一个软件一双击就会打开&#xff0c;只要软…

一文读懂PINGPONG:0成本在普通家用机上运行多个网络节点

概述 l pingpong项目设计的愿景便是降低去中心化网络的节点运行难度&#xff0c;让普通人也可以参与到节点运行中&#xff1b; l 这个周期里&#xff0c;随着Depin和AIWeb3叙事的爆发&#xff0c;众多中小型去中心化网络面临着节点运行者供需不匹配的问题&#xff0c;严重影响…

MySQL约束与事务

一、MySQL约束 参考【MySQL】约束_mysql约束-CSDN博客 not null&#xff1a;非空约束unique&#xff1a;唯一性约束primary key&#xff1a;主键约束foreign key&#xff1a;外键约束check&#xff1a;检查约束default&#xff1a;默认值约束 如何添加约束 在CREATE TABLE时…

zyx青岛实训day34 初步了解Docker与套接字的应用

回顾 1、主从复制&#xff08;高可用&#xff09; 2、传统的主从复制 3、gtids事务型的主从复制 4、注意 1&#xff09;server_id唯一 2&#xff09;8.X版本需要get_ssl_pub_key 3&#xff09;5.X不需要 4&#xff09;change master to 5&#xff09;stop | start slave 5…