news 2026/4/16 12:31:00

【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/6 0:41:24

维智 MCP 接口服务技术支持指南

&#xff08;一&#xff09;服务概述 维智 MCP Server 基于标准化 MCP 协议&#xff0c;整合丰富地理信息数据&#xff0c;为智能体提供地点搜索、正 / 逆地理编码、IP 定位解析、天气查询四大核心能力。数据覆盖城市级、街道级、坐标级查询&#xff0c;支持 HTTP/SSE 双传输协…

作者头像 李华
网站建设 2026/4/11 0:33:41

制造企业如何打通“三链”协同?一文讲清

目录 一、概念区分 1.产业链 2.供应链 3.价值链 二、那么&#xff0c;不协同具体会带来哪些问题&#xff1f; 1.目标不一致导致的行动矛盾 2.部门之间的优化相互冲突 3.创新想法受制于现实条件 三、怎么才能推动协同&#xff1f; 第一步&#xff0c;从统一目标和评价…

作者头像 李华
网站建设 2026/4/16 11:00:45

【老毛桃WinPE启动盘制作全攻略,附镜像获取渠道】

经常遇到电脑蓝屏、系统崩溃、忘记开机密码的情况&#xff1f;别急着送修&#xff01;一个WinPE启动盘就能轻松解决这些问题。今天就给大家带来超详细的老毛桃WinPE启动盘制作教程&#xff0c;从工具准备到系统安装&#xff0c;每一步都讲得明明白白&#xff0c;零基础小白也能…

作者头像 李华
网站建设 2026/4/16 11:15:39

FlutterOpenHarmony商城App底部导航栏组件开发

前言 底部导航栏是移动应用中最常见的导航模式之一&#xff0c;它为用户提供了在应用主要功能模块之间快速切换的能力。在商城应用中&#xff0c;底部导航栏通常包含首页、分类、购物车、我的等核心入口&#xff0c;用户可以通过点击不同的标签页快速访问对应的功能模块。本文…

作者头像 李华
网站建设 2026/4/14 22:28:30

COMSOL锂离子电池仿真技术与应用

在锂离子电池开发过程中&#xff0c;设计参数太多&#xff0c;实验任务繁重&#xff1b;各参数对电池性能的影响不明确&#xff0c;实验设计带有一定的盲目性&#xff0c;有时候甚至会出现费时、费力、费资金&#xff0c;却吃力不讨好的现象。改善这一状况的契机是将电池仿真技…

作者头像 李华
网站建设 2026/4/16 12:16:53

36、高级信号管理:深入理解与应用

高级信号管理:深入理解与应用 1. 信号集操作函数 1.1 基本信号集函数 在信号处理中,有两个初始化信号集的函数。 sigemptyset() 用于清空信号集, sigfillset() 则将所有信号包含在信号集中,它们都返回 0。在进一步使用信号集之前,需要调用这两个函数之一对信号集进…

作者头像 李华