Java 入门指南:JVM(Java虚拟机)——类的生命周期与加载过程

news2024/12/28 6:00:32

文章目录

    • 类的生命周期
    • 类加载过程
      • 1)载入(Loading)
      • 2)验证(Verification)
        • 文件格式验证
        • 符号引用验证
      • 3)准备(Preparation)
      • 4)解析(Resolution)
        • 符号引用
        • 直接引用
      • 5)初始化(Initialization)

JVM 运行 Java 代码的时候,需要将编译后的字节码文件加载到其内部的运行时数据区域中进行执行。这个过程涉及到了 Java 的类加载机制

![[Pasted image 20240915212223.png]]

类的生命周期

类从被加载到虚拟机内存中开始到卸载出内存为止,它的整个生命周期可以简单概括为 7 个阶段:

  1. 加载(Loading):类加载的过程由类加载器(ClassLoader)完成,类加载器负责将字节码文件(.class 文件)加载到 JVM 中。类加载器的主要职责包括:
  • 加载类文件:从指定位置读取字节码文件,并将其转换成字节数组。
  • 创建 Class 对象:将字节数组转换成 Class 对象,并将其存储在方法区中。
  1. 验证(Verification):验证阶段是为了确保类文件的数据符合 JVM 的规范,不会对 JVM 造成危害。验证主要分为四个阶段:
  • 文件格式验证:确保字节流的格式符合 Class 文件格式规范。
  • 元数据验证:确保类的元数据信息(如常量池中的常量)正确无误。
  • 字节码验证:确保字节码指令符合 JVM 规范,不会导致非法操作。
  • 符号引用验证:确保符号引用能正确解析到实际存在的类、接口、方法或字段。
  1. 准备(Preparation):准备阶段主要是为类变量分配内存空间,并设置类变量的初始值。注意,这里的类变量指的是被 static 修饰的变量。实例变量则是在对象实例化时分配内存空间。

  2. 解析(Resolution):解析阶段是将符号引用转换为直接引用的过程。符号引用指的是类名、接口名、方法名等字符串形式的引用,而直接引用则指向目标对象在内存中的地址。

  3. 初始化(Initialization):初始化阶段是执行类构造器 (<clinit>) 方法的过程。在这个阶段,类中的静态变量会被赋予初始值,并执行静态块中的代码。初始化阶段还包括类中非静态方法的调用和类的实例化。

  4. 使用(Using):类初始化完成后即可正常使用。此时可以创建类的实例,调用类的方法等。

  5. 卸载(Unloading):当一个类不再被引用,并且垃圾回收器确定没有对该类的引用后,类加载器会释放该类占用的资源,最终类会被卸载。卸载的过程是由 JVM 自动管理的,通常发生在内存压力较大时。

其中,验证、准备和解析这三个阶段可以统称为连接(Linking)。这 7 个阶段的顺序如下图所示:

![[一个类的完整生命周期.png]]

类加载过程

类的生命周期除去使用和卸载,就是 Java 的类加载过程。这 5 个阶段一般是顺序发生的,但在动态绑定的情况下,解析阶段发生在初始化阶段之后。系统加载 Class 类型的文件主要三步:加载->连接->初始化。连接过程又可分为三步:验证->准备->解析

![[Pasted image 20240915212736.png]]

1)载入(Loading)

类加载过程的第一步,主要完成下面 3 件事情:

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

即将字节码从不同的数据源(可能是 class 文件、也可能是 jar 包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的 java.lang.Class 对象。

虚拟机规范上面这 3 点并不具体,因此是非常灵活的。比如:“通过全类名获取定义此类的二进制字节流” 并没有指明具体从哪里获取( ZIPJAREARWAR、网络、动态代理技术运行时动态生成、其他文件生成比如 JSP…)、怎样获取。

加载这一步主要是通过 类加载器 完成的。类加载器有很多种,当我们想要加载一个类的时候,具体是哪个类加载器加载由 双亲委派模型 决定

每个 Java 类都有一个引用指向加载它的 ClassLoader。不过,数组类不是通过 ClassLoader 创建的,而是 JVM 在需要的时候自动创建的,数组类通过getClassLoader()方法获取 ClassLoader 的时候和该数组的元素类型的 ClassLoader 是一致的。

一个非数组类的加载阶段(加载阶段获取类的二进制字节流的动作)是可控性最强的阶段,这一步我们可以去完成还可以自定义类加载器去控制字节流的获取方式(重写一个类加载器的 loadClass() 方法)。

加载阶段与连接阶段的部分动作(如一部分字节码文件格式验证动作)是交叉进行的,加载阶段尚未结束,连接阶段可能就已经开始了。

2)验证(Verification)

JVM 会在该阶段对二进制字节流进行校验,只有符合 JVM 字节码规范的才能被 JVM 正确执行。该阶段是保证 JVM 安全的重要屏障,下面是一些主要的检查。

验证阶段这一步在整个类加载过程中耗费的资源还是相对较多的,但很有必要,可以有效防止恶意代码的执行。任何时候,程序安全都是第一位。

验证阶段主要由四个检验阶段组成:

  1. 文件格式验证(Class 文件格式检查)
  2. 元数据验证(字节码语义检查)
  3. 字节码验证(程序语义检查)
  4. 符号引用验证(类的正确性检查)

![[Pasted image 20240915213543.png]]

文件格式验证

文件格式验证这一阶段是基于该类的二进制字节流进行的,主要目的是保证输入的字节流能正确地解析并存储于方法区之内,格式上符合描述一个 Java 类型信息的要求。除了这一阶段之外,其余三个验证阶段都是基于方法区的存储结构上进行的,不会再直接读取、操作字节流了。

方法区属于是 JVM 运行时数据区域的一块逻辑区域,是各个线程共享的内存区域。当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。方法区会存储已被虚拟机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据

符号引用验证

符号引用验证发生在类加载过程中的解析阶段,具体来说是 JVM 将符号引用转化为直接引用的时候(解析阶段会介绍符号引用和直接引用)。

符号引用验证的主要目的是确保解析阶段能正常执行,如果无法通过符号引用验证,JVM 会抛出异常,比如:

  • java.lang.IllegalAccessError:当类试图访问或修改它没有权限访问的字段,或调用它没有权限访问的方法时,抛出该异常。

  • java.lang.NoSuchFieldError:当类试图访问或修改一个指定的对象字段,而该对象不再包含该字段时,抛出该异常。

  • java.lang.NoSuchMethodError:当类试图访问一个指定的方法,而该方法不存在时,抛出该异常。

3)准备(Preparation)

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。对于该阶段有以下几点需要注意:

  • 这时候进行内存分配的仅包括类变量( Class Variables ,即静态变量,被 static 关键字修饰的变量,只与类相关,因此被称为类变量),而不包括实例变量。实例变量会在对象实例化时随着对象一块分配在 Java 堆中。

  • 从概念上讲,类变量所使用的内存都应当在 方法区 中进行分配。不过有一点需要注意的是:JDK 7 之前,HotSpot 使用永久代来实现方法区的时候,实现是完全符合这种逻辑概念的。 而在 JDK 7 及之后,HotSpot 已经把原本放在永久代的字符串常量池、静态变量等移动到堆中,这个时候类变量则会随着 Class 对象一起存放在 Java 堆中。

JVM 会在该阶段对类变量(也称为静态变量static 关键字修饰的)分配内存并初始化,对应数据类型的默认初始值,"通常情况"下是数据类型默认的零值(如 00Lnullfalse 等)。但若给变量加上了 final 关键字,在准备阶段变量的值就被赋值为指定的值。

基本数据类型的零值

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

也就是说,假如有这样一段代码:

public String a = "Java";
public static String b = "Java";
public static final String c = "JVM";

a 不会被分配内存,而 b 会;但 b 的初始值不是“Java”而是 null

由于 static final 修饰的变量被称作为常量,和类变量不同。常量一旦赋值就不会改变了,所以 c 在准备阶段的值为“JVM”而不是 null

4)解析(Resolution)

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。 解析动作主要针对以下 7 类符号引用进行:

  • 类或接口
  • 字段
  • 类方法
  • 接口方法
  • 方法类型
  • 方法句柄
  • 调用限定符
符号引用

符号引用(Symbol References):以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局无关,引用的目标并不一定是已经加载到虚拟机内存当中的内容。

各种虚拟机实现的内存布局可以各不相同,但是它们能接受的符号引用必须都是一致的,因为符号引用的字面量形式明确定义在 Class 文件格式中。符号引用在编译时生成,存储在编译后的字节码文件的常量池中。

例如,在编译时,Java 类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。比如 com.Order 类引用了 com.Item 类,编译时 Order 类并不知道 Item 类的实际内存地址,因此只能使用符号 com.Item

直接引用

直接引用(Direct References):可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。通过对符号引用进行解析,找到引用的实际内存地址

直接引用是和虚拟机实现的内存布局直接相关的,同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标必定已经在虚拟机的内存中存在。由于直接指向了内存地址或者偏移量,所以通过直接引用访问对象的效率较高。

在程序执行方法时,系统需要明确知道这个方法所在的位置。Java 虚拟机为每个类都准备了一张方法表来存放类中所有的方法。当需要调用一个类的方法的时候,只要知道这个方法在方法表中的偏移量就可以直接调用该方法了。通过解析操作符号引用就可以直接转变为目标方法在类中方法表的位置,从而使得方法可以被调用。

5)初始化(Initialization)

初始化阶段是执行初始化方法 <clinit> () 方法的过程,该阶段是类加载过程的最后一步。在准备阶段,类变量已经被赋过默认初始值,而在初始化阶段,类变量将被赋值为代码期望赋的值。

例如:

String temp = new String("JVM");

上面这段代码使用了 new 关键字来实例化一个字符串对象,那么这时候,就会调用 String 类的构造方法对 temp 进行实例化。

public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

对于 <clinit> () 方法的调用,虚拟机会自己确保其在多线程环境中的安全性。因为 <clinit> () 方法是带锁线程安全,所以在多线程环境下进行类初始化的话可能会引起多个线程阻塞,并且这种阻塞很难被发现。

对于初始化阶段,虚拟机严格规范了有且只有 6 种情况下,必须对类进行初始化(只有主动去使用类才会初始化类):

  1. 当遇到 newgetstaticputstaticinvokestatic 这 4 条字节码指令时,比如 new 一个类,读取一个静态字段(未被 final 修饰)、或调用一个类的静态方法时。

    • 当 jvm 执行 new 指令时会初始化类。即当程序创建一个类的实例对象
    • 当 jvm 执行 getstatic 指令时会初始化类。即程序访问类的静态变量(不是静态常量,常量会被加载到运行时常量池)。
    • 当 jvm 执行 putstatic 指令时会初始化类。即程序给类的静态变量赋值
    • 当 jvm 执行 invokestatic 指令时会初始化类。即程序调用类的静态方法
  2. 使用 java.lang.reflect 包的方法对类进行反射调用时如 Class.forName("..."), newInstance() 等等。如果类没初始化,需要触发其初始化。

  3. 初始化一个类,如果其父类还未初始化,则先触发该父类的初始化。

  4. 当虚拟机启动时,用户需要定义一个要执行的主类 (包含 main 方法的那个类),虚拟机会先初始化这个类。

  5. MethodHandleVarHandle 可以看作是轻量级的反射调用机制,而要想使用这 2 个调用,就必须先使用 findStaticVarHandle 来初始化要调用的类。

  6. 当一个接口中定义了 JDK8 新加入的默认方法(被 default 关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。

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

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

相关文章

FreeRTOS—任务通知

一&#xff0c;概念介绍 队列、信号量、事件组等IPC技术都需要创建一个中间对象进程之间通过这些中间对象进行通讯或同步。创建对象就需要分配内存&#xff0c;占用一定内存。 二&#xff0c;任务通知的特点&#xff1a; 一个任务或ISR向另外一个指定的任务发送通知&#xff0c…

2024年最新版Vue3学习笔记

本篇文章是记录来自尚硅谷禹神2023年课程的学习笔记&#xff0c;不得不说禹神讲的是真的超级棒&#xff01; 文章目录 创建Vue3工程main.ts文件解析初始化项目写一个简单的效果 Vue3核心语法setup函数setup和选项式的区别setup语法糖指定组件名称 响应式数据ref函数定义基本类…

C#学习系列之Gmap地图界面上的实时绘制问题

C#学习系列之Gmap地图界面上的实时绘制问题 前言总结 前言 在地图控件上增加绘制不规则图形&#xff0c;在之前的经验来看&#xff0c; System.InvalidOperationException:“无法使用 DependencyObject&#xff0c;它属于其父 Freezable 之外的其他线程。” 其实就是ui线程中…

9.15javaweb项目总结

1.贴吧界面算是完成了基本的 能通过url打开多个贴吧信息的界面了&#xff0c;界面水平不是很高&#xff0c;界面还有待提升&#xff0c;然后该界面的功能点还差点有点远&#xff0c;完成度不是很高。 2.解决了关注的功能问题 要考虑的地方有点多&#xff0c;最简单的就是点击…

SpringSecurity 5

springSecurity是spring的一个顶级项目 也是一个安全框架&#xff0c;可以在spring框架中直接引用。 springSecurity基于RBAC用来处理登录功能和各种权限校验。 〇、配置和运行springSecurity 导入security启动器和web启动器&#xff0c;写一个springBoot的启动类&#xff0c;可…

VMamba: Visual State Space Model 论文总结

题目&#xff1a;VMamba: Visual State Space Model&#xff08;视觉状态空间模型&#xff09; 论文&#xff1a;[2401.10166] VMamba: Visual State Space Model (arxiv.org) 源码&#xff1a;https://arxiv.org/pdf/2401.10166 (github.com) 目录 一、摘要 二、引言 三、方…

URP 线性空间 ui资源制作规范

前言&#xff1a; 关于颜色空间的介绍&#xff0c;可参阅 unity 文档 Color space URP实现了基于物理的渲染&#xff0c;为了保证光照计算的准确&#xff0c;需要使用线性空间&#xff1b; 使用线性空间会带来一个问题&#xff0c;ui资源在unity中进行透明度混合时&#xff…

嵌入式常用轻量级校验算法

在嵌入式中涉及通信基本都需要用到校验算法&#xff0c;如&#xff1a;UART常用的奇偶校验、CAN通信常用的CRC校验等等。下面我们将介绍几种常用的校验算法&#xff1a; 一、校验和 校验和是最基本&#xff0c;也是嵌入式软件工程师最常用的一种校验算法&#xff0c;其实现方法…

jdk知识

jdk,jre,jvm jdk>jre>jvm jdk的bin目录下有编译工具&#xff0c;平时写完java文件用jdk编译&#xff1b; jre的lib文件夹里面是java的jar包(.class文件)&#xff0c;用来给jvm运行编译后的.class文件。 jvm&#xff1a;运行.class文件&#xff0c;.class文件读入到虚拟机…

JVM 垃圾回收机制和GC案例分析

1. 引言 Java 虚拟机&#xff08;JVM&#xff09;的垃圾回收&#xff08;Garbage Collection, GC&#xff09;机制&#xff0c;是自动内存管理的重要组成部分。它通过回收不再使用的对象&#xff0c;避免手动释放内存的麻烦。然而&#xff0c;随着系统复杂性的增加&#xff0c…

C/C++——野指针处理

在C++中,“野指针”(dangling pointer)指的是指向已释放或无效内存的指针。使用野指针可能导致程序崩溃或产生未定义行为。避免野指针的关键在于确保指针始终指向有效内存。下面是一些避免野指针的方法和最佳实践: 1、释放内存后置空指针 当释放掉分配的动态内存后,将指…

文本到3D生成

文本到3D生成是一种通过文本描述直接创建三维数字模型的技术。这种技术能够将语言描述转换成可视化的三维模型&#xff0c;使得内容创作者和设计师可以直接从概念阶段跳转到三维可视化&#xff0c;大大加快创作流程并提供更直观的设计和修改过程。 该技术的核心应用之一是基于…

【乐吾乐大屏可视化组态编辑器】使用手册

1 总览 开始设计&#xff1a;大屏可视化设计器 - 乐吾乐Le5le 1.1 画布 画布即绘画区域&#xff0c;将图形拖拽到画布进行编辑&#xff0c;绘制大屏。 1.2 菜单栏 顶部菜单导航&#xff0c;一级菜单可设置Logo、公司名称、文件编辑、常用编辑、查看、帮助&#xff0c;设置大…

MySQL篇(SQL - 分类)(持续更新迭代)

目录 简介 一、DDL 1. 简介 2. 数据库操作 2.1. 查库 查询所有数据库 查询当前数据库 2.2. 创库 2.3. 删库 2.4. 切库 3. 表操作 3.1. 查询 查询当前数据库所有表 查看指定表结构 查询指定表的建表语句 3.2. 创表 3.3. 改表 添加字段 修改数据类型 修改字段…

unity3d入门教程六

unity3d入门教程六 15.1预制体15.2编辑预制体15.3在场景中编辑15.4动态创建实例15.5实例的销毁16.1&#xff08;练习&#xff09;子弹发射16.2定时器16.3键盘事件 15.1预制体 火神山10天建成&#xff0c;使用了预制体技术 一个个小房间都是事先建造好的&#xff0c;最后吊车装…

Acrobat 2022 安装

软件介绍 Adobe Acrobat 是由Adobe公司开发的一款PDF&#xff08;Portable Document Format&#xff0c;便携式文档格式&#xff09;编辑软件。借助它&#xff0c;可以以PDF格式制作和保存文档&#xff0c;以便于浏览和打印&#xff0c;同时还可以使用一些高级工具来创建、编辑…

黑马十天精通MySQL知识点

一. MySQL概述 安装使用 MySQL安装完成之后&#xff0c;在系统启动时&#xff0c;会自动启动MySQL服务&#xff0c;无需手动启动。 也可以手动的通过指令启动停止&#xff0c;以管理员身份运行cmd&#xff0c;进入命令行执行如下指令&#xff1a; 1 、 net start mysql80…

Autosar E2E通信保护简介

文章目录 前言一、E2E基本概念二、为什么要做E2E?三、E2E保护的三种不同实现方式E2E TransformerE2E Protection Wrapper(E2EPW)COM E2E Callout四、E2E ProfileE2E Profile 01 机制E2E Profile 02 机制E2E Profile 04 机制E2E Profile 05 机制E2E Profile 06 机制E2E Profi…

828华为云征文|部署在线文件管理器 Spacedrive

828华为云征文&#xff5c;部署在线文件管理器 Spacedrive 一、Flexus云服务器X实例介绍1.1 云服务器介绍1.2 产品优势1.3 计费模式 二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 Spacedrive3.1 Spacedrive 介绍3.2 Docker 环境搭建3.3 Spac…

38900 机动车安全检测

参考文章&#xff1a;https://www.zhihu.com/question/458001000 机动车检查 《道路交通安全法实施条例》有关规定第十六条&#xff1a;机动车应当从注册登记之日起&#xff0c;按照下列期限进行安全技术检验&#xff1a; &#xff08;一&#xff09;营运载客汽车5年以内每年…