第七章——类加载机制

Zemax非序列模式新手入门:从零搭建一个抛物面反射镜系统(含灯丝光源与探测器设置) 本文详细介绍了如何在Zemax非序列模式下从零搭建抛物面反射镜系统,包括灯丝光源配置、反射镜建模和探测器设置等关键步骤。通过实战指南,帮助光学设计新手快速掌握非序列模式的应用技巧,特别适合处理复杂光线传播场景如反射镜系统和杂散光分析。 阅读详情

概述

  虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。

类加载的时机

  一个类从被加载到虚拟机内存开始,到卸载出内存为止,整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸载(Unloading)。其中,验证、准备、解析三个阶段统称为连接。

  加载、验证、准备、初始化、卸载这五个阶段的顺序是确定的,类的加载过程必须按照这个顺序开始(注意,是开始,不是“进行”或“完成”,这些阶段通常都是互相交叉的混合式进行的,会在一个阶段进行的过程中调用或激活另一个阶段)。解析阶段在某些情况下可以在初始化之后开始,这是为了支持Java语言的运行时绑定。

  没有规定什么时候开始进行类的“加载”阶段,但是严格规定了有且只有四种情况,必须立即开始初始化(相应的,加载、验证、准备自然要在这之前)。

  1. 遇到:new、getstatic、putstatic、invokestatic这4条字节码指令的时候,如果类没有初始化,就要先做类的初始化。生成这四条指令的Java代码最常见的是:使用“new”关键字实例化对象的时候;读取或者设置一个类的静态字段的时候(除非它被final修饰,在编译的时候已经把结果放在了常量池);调用一个类的静态方法的时候。
  2. 使用java.lang.reflect包中的方法对类进行反射调用的时候。
  3. 初始化一个类的时候发现父类还没有初始化,就要先初始化父类。
  4. 当虚拟机启动的时候,用户要指定一个要执行的主类,虚拟机会先初始化这个主类。

  举几个例子:通过子类使用父类的静态字段的时候,不会初始化子类,只会初始化父类,此时,要不要对子类进行加载和验证,虚拟机规范并没有规定,但是hotspot虚拟机是在此时加载了子类的;创建某个类的数组的时候,不会触发这个类的初始化,它会触发一个由编译器自动生成的类的初始化,这个自动生成的类里面包含一个数组应该有的属性和方法(但是用户只能访问public的length属性和clone方法),这个自动生成的类应该就是Java中代表数组的类;如果A类访问了B类被final修饰的静态字段,那么编译器会把A类中的访问结果直接存储到A类的常量池中,最后生成的字节码文件中,A类和B类是没有联系的。

  接口的初始化不能使用static语句块,但是编译器还是会为接口生成clinit()构造器用于初始化接口中定义的成员变量。接口初始化时不要求父接口必须全部初始化,只有真正使用到父接口的时候(如引用父接口中定义的常量)才会初始化父接口。

类加载的过程

1. 加载

  加载阶段虚拟机要做三件事:

  1. 通过一个类的全限定名来获取定义此类的二进制字节流。(没有规定字节流从哪里来,可以是压缩包,可以是网络,甚至可以用16进制编辑器写的二进制文件。)
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
  3. 在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。

  没有规定从何而来,所以可以使用系统提供的,或者用户自定义的类加载器来控制字节流的获取方式。加载阶段完成之后虚拟机外部的二进制字节流就按照虚拟机需要的格式(具体这个格式是什么,规范里面没有规定,看虚拟机自己的实现)存在方法区之中了。

2. 验证

  验证是连接阶段的第一步,目的是为了确保class文件中包含的信息符合虚拟机的要求,并不会威胁虚拟机自身的安全。

  不同虚拟机对验证的实现不一样,但是大致可以分为下面的四个阶段:文件格式验证、元数据验证、字节码验证、和符号引用验证。

  • 文件格式验证:检验是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理。大概包含:是否以魔数0xCAFEBABE开头;主次版本号是否在当前虚拟机处理范围之内;检测常量tag标志,看是否有不支持的常量类型;等等……
  • 元数据验证:对字节码描述的信息进行语义分析,确保描述的信息符合Java语言的规范要求。例如:是否继承了不被允许继承的类,是否实现了接口中的方法;等等……
  • 字节码验证:整个验证过程中最复杂的阶段,主要进行数据流和控制流的分析。分析方法体,保证运行过程中不会出现危害虚拟机安全的行为。例如:保证方法体中的跳转指令不会跳转到方法体以外的字节码指令上;保证方法体中的类型转换是安全的;等等……
    通过了校验不一定就能保证没问题,因为通过一段代码来判断另一段代码的逻辑是无法做到准确的。而且会耗费很多时间做校验,所以从jdk1.7开始,编译器编译Java代码的时候会附加一些校验信息以供虚拟机进行校验,以节省一些时间。如果能保证字节码文件没有问题,还可以设置虚拟机跳过这个阶段。
  • 符号引用验证:连接阶段的解析阶段,符号引用转化为直接引用的时候发生这个校验。一般校验:符号引用中通过字符串描述的全限定名能否找到对应的类;在指定类中是否存在符合方法的字段描述符和简单名称所描述的方法和字段;等等……

3. 准备

  准备阶段会为类级(也就是静态变量)变量赋初始值,注意,这个初始值不是代码中写的初始值,而是这种数据类型的零值,各种类型的零值如下所示:

数据类型零值
int0
long0L
short(short)0
char‘\u0000’
byte(byte)0
booleanfalse
float0.0f
double0.0d
referencenull

  除非代码中写了final,这个值会存在class文件的常量池中,在准备阶段会被赋值成常量池中的ConstantValue。
  如果不是final,又在代码中写了自己的初始值的话,赋值的putstatic指令会放在类构造器clinit中执行,也就是在初始化阶段才会执行。

4. 解析

  这个阶段要把符号引用转换成直接引用。规范中并没有规定解析要在什么时候进行,但是要保证在某些指令之前必须解析完成,所以虚拟机可以选择在类加载的时候就进行解析,或者用到符号引用的时候才解析。虚拟机要保证在同一个实体中,如果一个符号引用已经成功解析过,那么后续的解析请求就应该都成功,同样的如果第一次解析失败,那么其它指令对这个符号的解析请求也应该收到相同的异常。
  解析主要针对类和接口、字段、类方法、接口方法四种符号引用进行,对应常量池中的各类符号引用。

  • 类或接口的解析:在一个类D中,把符号N,解析成类或接口C。
  1. 如果C不是数组,那么虚拟机会把符号N传给D的类加载器加载C,期间可能触发别的加载动作,比如需要加载D的父类或者接口,一旦这个加载过程出了任何异常,解析过程就宣告失败。
  2. 如果C是数组,数组中的元素是对象的话,虚拟机会先按照步骤1加载这个类,然后再生成一个代表此数组维度和元素的数组对象。
  3. 最后,如果上面的步骤没有异常,那么还要验证访问权限,如果D不能访问C的话,会抛出异常。
  • 字段解析

  看的不是很明白,只理解到:字段肯定是属于某个类的,从字段的符号引用把它所属的类加载出来。剩余的内容要换本书看看。

  理解错了,大错特错,字段所属的类的意思是:如果有个类boy,里面有个静态的String属性sex,那么“sex字段所属的类”指的是boy这个类,不是String。

  我他妈的把“sex字段所属的类”,理解成String了,sex属于String嘛。看书的时候就很费解,淦。

  1. 首先要解析这个字段所属的类,对sex来说就是boy,记作C。
  2. 在C自己中寻找这个字段,如果找到了就返回这个字段的直接引用。
  3. 如果C实现了接口,就按照继承关系从下往上寻找字段。
  4. 如果还没找到,就从下往上搜索自己的父类寻找字段。
  5. 如果还没找到那就算查找失败了。

  实际上要是同时在接口和父类都有相同的字段的话,会编译失败。

  • 类方法解析

  同样,也要先解析出这个方法在哪个类里面,用C表示这个类。

  1. 如果在类方法表中发现class_index中索引的C是个接口,就会抛异常。
  2. 在类中查找这个方法,有的话就返回方法的直接引用。
  3. 如果上一步没找到,那么就在父类中查找。
  4. 否则,就在接口和父接口中查找,如果找到了说明类C是一个抽象类。查找结束,抛出AbstractMethodError异常。
  5. 上面都没找到,说明没有,抛出NoSuchMethodError。
  6. 最后,找到之后还要验证是否具有访问权限。
  • 接口方法解析

  和类方法解析很像,先要按接口方法表的class_index找到方法所属的接口,用C表示。

  1. 和类方法相反,如果class_index的索引指向了一个类,就抛异常。
  2. 否则,在接口C中查找。
  3. 如果没找到,就在接口C的父接口,包含Object类,中查找。
  4. 否则,查找失败。
  5. 接口方法默认public,所以应该不会抛出权限错误的异常。

5. 初始化

  类加载过程中,只有加载阶段才可以通过自定义的类加载器参与,到初始化阶段才开始执行用户代码。初始化阶段会执行程序员写的初始化代码,也就是执行类构造器clinit方法的过程。

  1. clinit方法是由编译器自动收集类中的所有类变量的复制动作和静态语句块中的语句合并产生的,编译器收集的顺序是语句在源文件中的顺序决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在静态语句块之后的变量在前面的静态语句块中可以赋值,但是不能访问。
  2. clinit方法与类的构造函数(也就是init)方法不同,它不需要显式的调用父类构造器,虚拟机会保证在自雷的clinit方法执行之前,父类已经执行完毕,所以虚拟机中第一个被执行的clinit方法的类肯定是Object类。
  3. 由于父类的clinit方法先执行,所以父类中定义的静态语句块要优先于子类的变量赋值操作。
  4. clinit方法对类或者接口来说不是必须的,如果一个类中没有静态语句块也没有对变量的赋值操作,那么编译器可以不为这个类生成clinit方法。
  5. 接口的clinit方法不要求父接口的先执行,而是用到的时候才执行父接口的clinit方法。接口的实现类也不要求先执行接口的clinit方法。
  6. 虚拟机会保证一个类的clinit方法在多线程环境中被正确的加锁和同步,多线程同时加载类的时候只会有一个线程执行初始化方法,其它的阻塞等待。

类加载器

  类加载器和类自身共同决定了类的唯一性(class文件一样,但是类加载器不一样,他们也不算同一个类),Java开发人员看来,加载器有三种:

  • 启动类加载器(Bootstarp ClassLoader):负责加载<JAVA_HOME>\lib目录中的,或被-Xbootclasspath参数指定的路径中的,虚拟机识别(按文件名识别,例如rt.jar)的类库。
  • 扩展类加载器(Extension ClassLoader):由sun.misc.Launcher$ExtClassLoader实现,负责加载<JAVA_HOME>\lib\ext目录中的,或被java.ext.dirs系统变量所指定的路劲中的所有类库。开发者可直接使用。
  • 应用程序类加载器(Application ClassLoader):由sun.misc.Launcher$AppClassLoader实现,负责加载用户类路径(ClassPath)上所指定的类库。开发者可直接使用。
  1. 双亲委派模型:除了顶层的启动类加载器之外,其他的类加载器都要有自己的父类加载器(父子关系一般不用继承,而是用组合关系)。

  双亲委派模型的工作过程是:如果一个类加载器收到了一个加载请求,它首先不会自己尝试去加载,而是交给父类去加载,只有当父类反馈无法加载的时候自己才会尝试加载。
  有一个好处是Java类随着它的类加载器有了一种层级结构,例如Object类,无论哪一个类都要加载Object,双亲委派机制可以保证Object类永远都是启动类加载器加载的,在各种类加载环境下都是同一个Object类。

  1. 破坏双亲委派模型

  第一次:在双亲委派模型出现之前,程序员可以重写ClassLoader里面的loadClass()方法去加载一个类。
  第二次:出现了类库中的代码要加载用户代码的时候(启动类加载器要加载一个应用程序加载器才能加载得到的类,这显然加载不到啊),于是有了线程上下文加载器,这个加载器可以在创建线程时设置,如果不设置就从父线程继承一个,如果整个应用程序都没有,那么就默认是应用程序加载器。
  第三次:热更新,热部署OSGi,有自己不同于双亲委派模型的类加载顺序。

深入介绍 SAP OData CSRF Token 的一些技术细节 跨站请求伪造(Cross-Site Request Forgery,简称 CSRF)是一种网络攻击手段,它允许攻击者伪造用户的身份来执行未经授权的操作。为了防止 CSRF 攻击,常用的一种技术手段就是 CSRF Token.CSRF Token 是一种随机生成的防伪 Token,它由服务器颁发给客户端,并需要客户端在提交某些特定类型的请求时,将该 Token 添加到 HTTP 请求的头部字段去。 阅读详情

相关推荐

波束管理 Beam Management

Beam Management in 5G NR   波束管理,是5G NR中基于信道质量来动态选择每个UE和gNB之间以什么方向和频率的波束beam来进行通信。   背景   5G NR(New Radio)支持非常高的数据速率和更低的延迟。5G NR支持FR1和FR2频段,FR1为亚6 GHz(450至6000 MHz);FR2为毫米波段(24.25 GHz至52.6 GHz)。   由于毫米波频带使用非常高的频率,因此会导致传播损耗和其他损耗。为了补偿损失,在这种频率下定向通信是必不可少的。

最忆是江南的博客 5825

第七章 类加载过程

1.类加载过程:加载、验证、准备、解析、初始化 虚拟机的类加载机制:把描述类的数据从Class文件(或者其他途径)加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。 加载: 1.通过一个类的全限定名获取定义此类的二进制字节流 2.将这个字节流所代表的静态结构转化为方法区的运行时数据结构 3.在内存中生成一个代表这个类的java.lang.Class对象(并...

jiesi008的博客 329

3D医学影像分割新突破:手把手教你用MedSAM2快速标注CT/MRI数据(附3D Slicer插件配置)

本文详细介绍了如何利用MedSAM2模型进行3D医学影像分割,包括从环境准备到3D Slicer插件配置的全流程。通过实战案例和技巧分享,帮助医疗从业者快速掌握CT/MRI数据的高效标注方法,提升医学影像分析的准确性和效率。

ujm5678901的博客 298

javax.servlet.ServletException: java.lang.AbstractMethodError

使用MyEclipse部署项目遇到以下问题 javax.servlet.ServletException: java.lang.AbstractMethodError: getJspApplicationContext冲突解决方案 javax.servlet.ServletException: java.lang.AbstractMethodError: getJspAppli

pyjfoot的专栏 1万+

向量库深度解析:选型、索引原理与生产实践

本文系统解析五大主流向量库(pgvector、Milvus、Weaviate、ChromaDB、Faiss)的定位、优缺点与适用场景,深入拆解FLAT、IVF、HNSW等核心索引原理,并重点分析Milvus的Compaction机制。针对数据治理场景,提供精准选型建议:中小规模优先pgvector或Weaviate,超大规模选择Milvus,开发测试推荐ChromaDB。核心索引推荐HNSW,兼顾检索速度与召回率。全文从理论到实践,为开发者提供清晰的向量库选型逻辑与生产落地指南。

a123147abc的博客 360

Ubuntu服务器通过IPV6端口转发搭建幻兽帕鲁私服实战

本文详细介绍了在Ubuntu服务器上通过IPV6端口转发搭建幻兽帕鲁私服的完整教程。针对只有公网IPV6地址的服务器环境,提供了从硬件检查、端口转发工具配置到游戏服务器安装的全流程解决方案,帮助玩家轻松实现多人联机游戏体验。

weixin_30652491的博客 489

第七章 虚拟机类加载机制

但是jdk中有类A就需要使用这个类,实际运行的时候肯定不是用的接口,而是实现类,在加载类A的时候是bootstrap加载的发现类B的实现类C还未加载,则去加载类C,这里默认先用类A的加载器去加载,类C并不是在bootstrap加载的范围内的,而且按照双亲委派机制,是要从bootstrap往上找加载器的,很显然找不到,所以只能从线程上下文类加载器去加载。这阶段的验证是基于二进制字节流进行的,只有通过了这个阶段的验证之后,这段字节流才被允许进入 Java 虚拟机内存的方法区中进行存储,但是对于初始化阶段,

flyingmylife的博客 905

libfranka---joint_impendence_control例程分析

joint_impedance_control.cpp例程分析 部分内容来自链接,我个人以初学者的角度对代码进行了更加细致的解释。 核心部分: // Set and initialize trajectory parameters. const double radius = 0.05; const double vel_max = 0.25; const double acceleration_time = 2.0; const double run_time = 20.0; d

weixin_41567783的博客 1121

第七章类加载机制和类加载器

在Java的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器,来定制类的加载方式。为什么要自定义类加载器?隔离加载类在某些框架内进行中间件与应用的模块隔离,把类加载到不同的环境。比如:阿里内某容器框架通过自定义类加载器确保应用中依赖的jar包不会影响到中间件运行时使用的jar包。再比如:Tomcat这类Web应用服务器,内部自定义了好几种类加载器,用于隔离同一个Web应用服务器上的不同应用程序。修改类加载的方式。

一名在校大三学习JAVA 702

转子动力学 | 临界速度与稳定性分析

本文深入探讨了转子动力学中的核心问题——临界速度与稳定性分析。临界速度是旋转机械发生共振的危险转速,通过模态分析和坎贝尔图可进行预测。文章还剖析了油膜涡动等稳定性问题,并结合ANSYS仿真案例,详细阐述了从建模、分析到设计优化的完整工程实践流程,为旋转机械的可靠设计与安全运行提供关键指导。

weixin_29313239的博客 517

Win10或Win11电脑找不到以太网网络适配器的解决方法

Win10或Win11电脑找不到以太网网络适配器的解决方法

Robot_PLC_HMI_Automation 651

第七章、虚拟机类加载机制

深入理解Java虚拟机第七章总结。

撰写Bug 152

基于Python的雪球网投资组合数据爬取与分析实战:从入门到精通

在当今数字化投资时代,数据已成为投资者最重要的资产之一。雪球网作为中国领先的投资社交平台,汇聚了大量优质的投资组合数据,这些数据对于量化投资研究和个人投资决策具有重要价值。本文将详细介绍如何使用Python最新技术栈构建一个高效、稳定的雪球网投资组合爬虫,并深入分析获取的数据。投资组合持仓明细及变动历史投资收益率曲线与风险指标投资者操作策略与调仓频率热门行业与个股关注度。

2201_76125261的博客 920

第七章:虚拟机类加载机制

第一部分 概述:Class文件中描述的各种信息都需要加载到虚拟机中才能运行和使用。 但是具体如何加载到虚拟机中? Class文件加载到虚拟机中之后会发生什么事情? 虚拟机class文件加载到内存中,并且对class文件中的数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用 的java...

ninety_two的博客 132

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

第七章-虚拟机类加载机制-类加载的过程

7.1 概述 什么是虚拟机的类加载机制? 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型(机器码),这就是虚拟机的类加载机制。 Java源文件 ——> 编译器 ——> 字节码文件(Class文件) 字节码文件 ——> JVM ——> 机器码 7.2 类加载的时机 类的生命周期: 加载、验证、准...

lwycc2333的博客 314

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

第七章-虚拟机类加载机制

7.1概述 虚拟机的类加载机制:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。 在Java语言中,类型的加载、连接和初始化过程都是在程序运行期间完成的。 7.2类加载的时机 类的生命周期:加载---->验证---->准备---->解析---->初始化---->使用---->卸载。 ...

营长的博客 139

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

JVM第七章-虚拟机类加载机制

那。

weixin_44925329的博客 1140

【JVM】第七章 虚拟机类加载机制

  第七章 虚拟机类加载机制 https://blog.youkuaiyun.com/luanlouis/article/details/50529868 一、概述 class文件中描述的各种信息,最终都要加载到虚拟机中之后才能运行和使用。 类加载机制:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型。 类的加载过程是在...

weixin_38403493的博客 204

Java虚拟机 第七章之虚拟机类加载机制

#7.1 概述 问题: 1.虚拟机如何加载class文件? 2.class文件中的信息进入到虚拟机后会发生什么变化? 什么是虚拟机类加载机制? 书中写到:虚拟机把描述类的数据从class文件中加载到内存,并对数据进行验证、转化解析和初始化,最终形成被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。 在Java语言中,类的加载、连接和初始化过程都是在程序运行期间完成的,这种策略虽...

aLivable_Dedode的博客 319

深入理解Java虚拟机-第七章、虚拟机类加载机制

文章目录第七章、虚拟机类加载机制一、类加载的时机对类初始化的5个时机(有且只有这五个)被动引用的例子通过子类引用父类的静态变量,不会导致子类初始化通过数组定义来引用类,不会触发此类的初始化调用类的常量不会被初始化(static final)二、类加载的过程1、加载2、验证阶段验证步骤1、文件格式验证验证步骤2、元数据验证验证步骤3、字节码验证验证步骤4、符号引用验证3、准备阶段4、解析阶段5、 初...

weixin_38367817的博客 575
上一篇: JAVA基础——集合
下一篇: 第十二章——Java内存模型与线程
Suzuki Mirai
博客等级 码龄9年 17粉丝 55原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值