首页 > 其他分享 >7 JVM类加载机制

7 JVM类加载机制

时间:2022-11-13 21:02:35浏览次数:39  
标签:初始化 字节 验证 虚拟机 接口 JVM 机制 加载

目录

本章学些目标

  1. jvm如何加载字节码文件?
  2. 字节码文件的内容如何被jvm读取?

1 类/接口加载机制

虚拟机的类/接口加载过程

虚拟机把描述类的数据从字节码加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类/接口

如图所示:

注意:jvm先把类/接口编译成字节码文件,然后在运行期间,从字节码文件中完成加载过程。

1 类加载的时机

1.1 类的生命周期

  1. 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载 、验证、准备、解析、初始化 、使用、卸载 七个阶段。
  2. 验证、准备、解析三个部分统称为连接(Linking)
  3. 加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,解析可能先于初始化,这是由“运行时绑定特性”决定的

1.2 类初始化的时机

以下六种情况,必须进行类的初始化(包括加载、验证等阶段):(前提是类还没初始化)

  1. 遇到new、getstatic、putstatic或invokestatic这四条字节码指令时,包括:
    1. 使用new关键字实例化对象
    2. 读取或设置类的静态字段
    3. 调用类的静态方法
  2. 使用java.lang.reflect包的方法对类进行反射调用
  3. 当初始化类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
  4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化主类
  5. 如果一个java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句柄,则需要先触发其初始化。
  6. 如果接口定义了默认方法(default关键字),如果这个接口的实现类发生了初始化,那该接口要在其之前被初始化【todo:如何从内存层面理解?】

3 接口初始化的时机

接口的加载过程与类加载过程稍有不同,针对接口需要做一些特殊说明:

  1. 跟类的初始化过程基本一致。
  2. 接口跟类的初始化差异:
    1. 子类初始化时一定先初始化父类
    2. 接口在初始化时,并不要求其父接口已经初始化,只有在真正使用到父接口的时(如引用接口中定义的常量)才会初始化

4 类加载的过程

类加载的过程包括:加载、验证、准备、解析和初始化这五个阶段所执行的具体动作。

4.1 加载

虚拟机规范:加载过程需要完成这三件事:

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

通过验证阶段后,才会真正写入方法区

4.2 验证

  1. 确保Class文件的字节流信息符合Java虚拟机规范,保证安全。
  2. 优化方案:如果全部代码都已经被反复使用和验证过,在生产环境的实施阶段就可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

验证过程4个阶段:

只有通过了【文件格式验证】阶段之后,这段字节流才被允许进入方法区中进行存储,所以后面的三个验证阶段全部是基于方法区的存储结构上进行的,不再直接读取字节流

  1. 文件格式验证:验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理
    1.  是否以魔数0xCAFEBABE开头
    2.  主、次版本号是否在当前虚拟机接受范围之内
    3.  常量池的常量中是否有不被支持的常量类型
    4.  指向常量的各种索引值中是否有指向不存在的常量或不符合类型的常量
    5.  CONSTANT_Utf8_info型的常量中是否有不符合UTF-8编码的数据。
    6.  Class文件中各个部分及文件本身是否有被删除的或附加的其他信息
    7.  等等
  1. 元数据验证:对类的元数据信息进行语义校验,保证不存在与《Java语言规范》定义相悖的元数据信息
    1.  这个类是否有父类(除了java.lang.Object之外,所有的类都应当有父类)。
    2.  这个类的父类是否继承了不允许被继承的类(被final修饰的类)
    3.  如果这个类不是抽象类,是否实现了其父类或接口之中要求实现的所有方法
    4.  类中的字段、方法是否与父类产生矛盾(例如覆盖了父类的final字段,或者出现不符合规则的方 法重载,例如方法参数都一致,但返回值类型却不同等)
  1. 字节码验证:通过数据流分析和控制流分析,确定程序语义是合法的。第二阶段完成元数据校验,第三阶段对类的方法体(Code属性)进行校验分析
    1.  保证操作数栈的数据类型与指令代码序列能配合工作,例如不会出现“在操作栈放置了一个int类型的数据,使用时却按long类型来加载入本地变量表中”这样的情况
    2.  保证任何跳转指令都不会跳转到方法体以外的字节码指令上
    3.  保证方法体中的类型转换总是有效的,例如可以把一个子类对象赋值给父类数据类型,这是安全的,但是把父类对象赋值给子类数据类型,或者其它类型,则是非法的。
  1. 符号引用验证:对类自身以外(常量池中的各种符号引用)的各类信息进行匹配性校验,通俗来说就是,该类是否缺少或者被禁止访问它依赖的某些外部类、方法、字段等资源。(当前类依赖的信息(其它类)能否得到满足
    1. 符号引用中通过字符串描述的全限定名是否能找到对应的类
    2. 在指定类中是否存在符合方法的字段描述符及简单名称所描述的方法和字段
    3.  符号引用中的类、字段、方法的可访问性(private、protected、public、<package>)是否可被当 前类访问
    
    无法通过符号引用验证,虚拟机抛出java.lang.IncompatibleClassChangeError的子类异常,典型的如: java.lang.IllegalAccessError、java.lang.NoSuchFieldError、java.lang.NoSuchMethodError等。

4.3 准备

目标:

  1. 类变量分配内存并设置初始值(零值)
  2. 如果类变量的存在ConstantValue属性,那在准备阶段就会赋值(实际的值)(final修饰)

关于初始值的说明:

public static int value = 123;
//那变量value在准备阶段过后的初始值为0而不是123,因为这时尚未开始执行任何Java方法,把value赋值为123的putstatic指令是程序被编译后,存放于类构造器<clinit>()方法之中,所以把value赋值为123的动作要到类的初始化阶段才会被执行

4.4 解析

目标:

  1. Java虚拟机将常量池内的符号引用替换为直接引用

两者对比:

内存目标 形式
符号引用 与JVM的内存布局无关,引用的目标并不一定是已经加载到JVM内存中的内容 任何形式字面量
直接引用 和JVM实现的内存布局直接相关 指针、相对偏移量、能间接定位到目标的句柄

4.4.1 类或接口的解析

假设A存在对B类的引用,解析过程如下:

4.4.2 字段解析

4.4.3 方法解析

4.4.4 接口方法解析

4.5 初始化

待续

标签:初始化,字节,验证,虚拟机,接口,JVM,机制,加载
From: https://www.cnblogs.com/knowledgeispower/p/16886933.html

相关文章

  • jvm调优思路及调优案例
    jvm调优思路及调优案例​ 我们说jvm调优,其实就是不断测试调整jvm的运行参数,尽可能让对象都在新生代(Eden)里分配和回收,尽量别让太多对象频繁进入老年代,避免频繁对老年代......
  • 异步加载与请求
    异步加载与请求:文章目录​​异步加载与请求:​​​​背景:​​​​异步加载​​​​JSON介绍与应用​​​​异步GET与POST请求​​背景:随着技术的不断进步,现在不少网站已经引......
  • defer+recover机制处理错误
    defer+recover机制处理错误Go中追求代码优雅,引入机制:defer+recover机制处理错误内置函数recover:packagemainimport"fmt"funcmain(){ test() fmt.Println("上......
  • Day3-4 包机制,JavaDoc
    包机制为了更好地组织类,java提供包机制,用于区别类名的命名空间鲍鱼举的语法格式为 packagepkg1[.pkg2[.pkg3...]] 一般利用公司域名倒置作为包名......
  • jvm类加载
    类加载器:(由高到低)bootstrapclassloader;extclassloader;appclassloader不同类加载器有不同的作用(加载基础类库;拓展类库;三方类;自定义类),因此,每个类的字节码对象......
  • python的垃圾回收机制
    python对内存回收引用几个概念计数器:当python程序运行时,会根据数据类型的不同找到相对应的结构体,根据结构体中的字段来进行创建相关的数据。然后将对象添加到refchain双向......
  • uView list 控件分页加载出现抖动问题解决方案
    使用u-list 组件 动态加载数据时 滑动列表元素 会出现抖动的情况解决 设置preLoadScreen为根据page的动态变换就可以了preLoadScreen 列表前后预渲染的屏数,1......
  • 谈一谈 vuex 的运行机制
    Vuex提供数据(state)来驱动视图(vuecomponents),通过dispath派发actions,在其中可以做一些异步的操作,然后通过commit来提交mutations,最后mutations来更改state。 ......
  • 浅谈性能优化之图片压缩、加载和格式选择
    原文链接:浅谈性能优化之图片压缩、加载和格式选择在认识图片优化前,我们先了解下【二进制位数】与【色彩呈现】的关系。二进制位数与色彩在计算机中,一般用二进制数来表......
  • WPF 动态加载用户控件
    //这里可以动态加载其他dll文件中的组件Assemblyassem=Assembly.LoadFile($"{Directory.GetCurrentDirectory()}\\{data.DllName}");varonePage=assem.CreateInst......