news 2026/4/3 6:29:04

【JavaSE】十九、JVM运行流程 类加载Class Loading

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【JavaSE】十九、JVM运行流程 类加载Class Loading

文章目录

  • Ⅰ. 运行时数据区(内存布局)
  • Ⅱ. JVM 运行流程
    • ⭐ 大致流程
    • 一、类加载(Class Loading)
    • 二、执行引擎(Execution Engine)
    • 三、运行时数据区(Runtime Data Area)
    • 四、本地接口(Native Interface)
    • 五、垃圾回收(Garbage Collection, GC)
  • Ⅲ. 类加载 `Class Loading`
    • 一、类加载过程
    • 二、`.class` 文件的结构
    • 三、类加载器
    • 四、双亲委派模型(Parent Delegation Model)

Ⅰ. 运行时数据区(内存布局)

一个Java程序对应一个JVM而一个JVM对应一套数据区

  1. 程序计数器PC Register这只是一个很小的内存空间,保存着下一条执行的指令的地址

    1. 它是每个线程私有的,属于线程隔离的数据。
    2. 如果正在执行的是native方法,则这个值是未定义的。
    3. 它的存在是为了解决线程切换后能恢复到正确的执行位置,因此对多线程执行非常关键。
  2. 虚拟机栈JVM Stack:存放了方法调用的关系。

    1. 虚拟机栈是线程私有的。
    2. JVM栈中每一个元素,称为栈帧Stack Frame),每个方法在调用时都会创建一个新的栈帧。比如:局部变量,当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。
    3. JVM会抛出如StackOverflowErrorOutOfMemoryError,都和该区域有关。
    4. 栈帧中包含:
      • 局部变量表
      • 操作数栈
      • 动态连接
      • 方法返回地址
      • 其他信息,保存的都是与方法执行时相关的一些信息。
  3. 本地方法栈Native Method Stack用于支持Native方法的调用

    1. 本地方法栈是线程私有的。
    2. 本地方法栈与虚拟机栈的作用类似,只不过保存的内容是Native方法的局部变量。
    3. 底层是C/C++实现的。
    4. 在有些版本的JVM实现中(例如HotSpot),本地方法栈和虚拟机栈是一起的。
  4. HeapJVM中最大的内存区域,保存使用new创建的对象实例

    1. 堆是线程共享的。
    2. 堆的生命周期:堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。
    3. 堆是垃圾回收器的主要工作场所。
    4. 堆细分为:新生代(一个Eden、两个Survivor老年代
    5. 堆中每个对象不只是包含字段值,在每个对象的开头还保存了对象头结构,用来管理对象运行时信息的元数据容器,每个Java对象在内存中分为三部分:
      • 对象头Header):存储锁状态、GC 分代年龄、哈希码等
      • 实例数据Instance Data):对象的字段值
      • 对齐填充Padding):补齐内存对齐
  5. 方法区(Method Area存储类的元数据信息对象实例存放在堆中,方法区只是存放类的元数据

    1. 方法区是线程共享的。

    2. 实际上在Java8以后,方法区被 “元数据区” 替代,从原来的 “堆内存” 中移到 “本地内存” 中。

    +-------------------------------+ | 操作系统分配的总内存(物理RAM)| +-------------------------------+ | | | +------------------+ | | +--------v------+ +--------------v------------------+ | JVM 管理区域 | | JVM 外部使用但不直接管理(本地内存)| | | | | | - 堆(heap) | | - 元空间(Metaspace) | | - 栈(stack) | | - DirectBuffer / JNI 内存 | | - 程序计数器 | | - JIT 编译缓存 / 线程内部结构 | +--------------+ +----------------------------------+
    1. 存储内容包括:

      • 类的结构信息(字段、方法、接口、访问修饰符等等)
      • 静态变量
      • 运行时常量池(字面量、符号引用)
      • JIT 编译后的代码(在某些实现中)

Ⅱ. JVM 运行流程

⭐ 大致流程

JVM执行流程大概如下所示:

口诀:编译成 class,加载做五步,解释+JIT,运行靠内存,回收靠 GC。

一、类加载(Class Loading)

类加载是JVM.class文件(字节码)加载进内存,并在内存中生成一个Class对象的全过程,包括下面三个主要阶段:

  1. 加载:通过类加载器把字节码读进来,生成Class对象。
  2. 链接:分为验证、准备、解析三个子阶段。
  3. 初始化:执行<clinit>静态代码块和静态变量赋值。

二、执行引擎(Execution Engine)

JVM核心部件,负责执行字节码:

  • 解释执行:将字节码一行一行解释为机器指令。
  • 即时编译(JIT):将热点代码编译为本地机器码,提高性能。

三、运行时数据区(Runtime Data Area)

JVM内存模型,运行时管理所有数据:

区域作用描述
方法区(Method Area)存储类的信息(类元数据)、常量、静态变量等
堆(Heap)存储对象实例,是垃圾回收的主要区域
虚拟机栈(Stack)每个线程私有,存储方法调用的信息(帧栈)、局部变量等
本地方法栈为执行 native 方法准备
程序计数器(PC)每个线程私有,记录当前线程所执行的字节码行号

四、本地接口(Native Interface)

JVM可以调用本地语言(如 C、C++)写的函数,依赖JNIJava Native Interface)。

五、垃圾回收(Garbage Collection, GC)

  • 自动管理堆内存,回收无用对象
  • 分代回收:新生代、老年代
  • 常见算法:标记-清除、复制、标记-整理等

Ⅲ. 类加载Class Loading

一个类在什么时候触发 “加载” 呢❓❓❓

① 构造某个类的实例时

② 调用类的静态方法/静态成员时

③ 使用子类时,也会触发父类的加载

对于一个类来说,它的生命周期是这样子的:

其中前五步是固定的顺序,也是类加载的过程!

一、类加载过程

  1. 加载(Loading)
    1. JVM根据类的 “全限定名”,委托给类加载器去加载该类,并遵循双亲委派模型
    2. 然后类加载器从.class文件中读取字节流
    3. 最后JVM将字节流解析为Class对象,放入方法区中(这不等于立即解析所有内容)
  2. 验证(Verification)
    1. 确保类的字节码是合法的,防止恶意代码执行:
      • 文件格式验证(确保这是一个 “能看懂” 的.class文件,不是乱写的二进制)
      • 元数据验证(确保类的结构层次是 “符合 Java 语义的”)
      • 字节码验证(防止非法操作、恶意字节码,比如篡改局部变量、栈顶数据,JVM崩溃)
      • 符号引用验证(为解析阶段提前做准备,避免运行时解析失败)
  3. 准备(Preparation)
    1. 为类的静态变量分配内存,并设置默认初始值(不是代码中的赋值)。比如static int a = 10;只分配内存,初始化为默认值0,而还没到赋值为10的阶段。
  4. 解析(Resolution)
    1. 将常量池中的符号引用(如类名、字段名)转换为实际的直接引用(内存地址),即对常量进行初始化
  5. 初始化(Initialization)
    1. 执行类的构造方法(类构造器),即静态变量赋值、静态代码块执行等。

二、.class文件的结构

.class文件是一个以字节为单位的严格二进制格式的结构体,包含类的所有元信息方法字节码常量池等。如下所示:

ClassFile{u4 magic;// 魔数,标识文件的类型u2 minor_version;// 次版本号u2 major_version;// 主版本号u2 constant_pool_count;// 常量池数量cp_info constant_pool[];// 常量池,包括字符串、类名、方法名、字段名等,最核心的部分u2 access_flags;// 访问标志(public, abstract, final等)u2 this_class;// 当前类的索引(指向常量池)u2 super_class;// 父类索引(指向常量池)u2 interfaces_count;// 实现接口数量u2 interfaces[];// 接口表u2 fields_count;// 字段数量field_info fields[];// 字段表u2 methods_count;// 方法数量method_info methods[];// 方法表u2 attributes_count;// 属性数量attribute_info attributes[];// 属性表(如源码信息、注解、行号表等)}

三、类加载器

类加载器(ClassLoader)是类加载机制的核心,本质上就是一个 "负责读取类的字节流并交给JVM转换为Class对象" 的工具

JVM提供了三种主要的类加载器

类加载器作用
Bootstrap ClassLoader启动类加载器:加载 java 核心类库
Extension ClassLoader扩展类加载器:加载拓展目录下的类(jdk 自带但不是标准约定的库)
Application ClassLoader应用类加载器:加载你写的代码和第三方库类

四、双亲委派模型(Parent Delegation Model)

在类加载的加载阶段JVM会根据类的全限定名,通过类加载器加载该类。类加载器在加载类时默认遵循 “双亲委派模型”,即优先让父类加载器尝试加载类,只有在父加载器无法加载时,才由当前加载器尝试加载。

原则:从下往上委托,然后先父后子,从上往下加载

过程如下所示:

  1. 当前类加载器先将请求委托给父类加载器,注意是层层委托上去,直到最高层加载器。
  2. 然后从最高层父类加载器开始加载class文件,如果加载不到,则一层一层往下尝试是否能进行加载。

这样子做是为了保证:

  1. java.lang.ObjectString这类核心类一定由启动类加载器加载,避免用户自定义的类覆盖核心类,提升安全性。
    1. 比如用户伪造了一个java.lang.String,但加载器是从上往下的,此时会先加载父类中的java.lang.String,而不会加载到用户伪造的那个版本!
  2. 避免类的重复加载
    1. 比如 A 类和 B 类都有一个父类 C 类,那么当 A 启动时就会将 C 类加载起来,那么在 B 类进行加载时就不需要在重复加载 C 类了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/3 4:20:41

Nacos 服务发现保证机制解析

Nacos 通过多重机制确保新注册的服务能够被及时发现。让我详细讲解其工作原理和保证机制&#xff1a; 一、核心发现流程 新服务注册 → Nacos Server → 服务发现客户端 → 缓存更新 → 负载均衡 → 流量转发 二、Nacos 服务发现保证机制 1注册中心层面的保证// Nacos Server 内…

作者头像 李华
网站建设 2026/3/31 4:16:38

解析现代网络的“神经系统”—BGP-LS-SPF

引言 随着现代以太网的发展和规模扩大&#xff0c;新的节点加入或网络升级等变化不断发生。另外现代以太网的空间环境复杂&#xff0c;链路状态可能会因空间天气、卫星轨道调整、节点链路调整以及其他因素而变化&#xff1b;BGP-LS-SPF 能够适应这些动态变化&#xff0c;新节点…

作者头像 李华
网站建设 2026/3/31 0:15:49

SAM2跟踪的理解9——mask decoder

目录 一、前言 四、MaskDecoder.forward 4.1 MaskDecoder.predict_masks 4.1.2 TwoWayTransformer.forward 4.1.2.11 TwoWayAttentionBlock.forward 4.1.2.12 self.final_attn_token_to_image——Attention.forward attn_out self.final_attn_token_to_image(qq, kk, vk…

作者头像 李华
网站建设 2026/4/3 0:22:51

五度易链产业大脑:从数据融合到智能决策的技术实践

在数字经济浪潮中&#xff0c;数据已不仅是信息载体&#xff0c;更是继劳动力、技术、资本和土地之后的“第五大生产要素”。企业日常运营中产生的各种数据&#xff0c;不仅被视为数字经济最核心的资源&#xff0c;而且已经成为了企业的重要资产。如何从海量数据中挖掘价值&…

作者头像 李华
网站建设 2026/3/22 17:13:15

23、资源限制与线程编程:原理、模式与应用

资源限制与线程编程:原理、模式与应用 在计算机编程领域,资源限制和线程管理是至关重要的概念。合理设置资源限制能确保程序在可控的资源范围内运行,避免资源耗尽;而线程编程则能提高程序的性能和响应能力,但也带来了一些挑战。本文将详细介绍资源限制的设置与获取,以及…

作者头像 李华