news 2026/6/9 9:51:10

JVM 核心知识

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JVM 核心知识

JVM 核心知识

一、类加载子系统

1.1 类加载完整生命周期

JVM 采用懒加载机制,类不会在启动时一次性全部加载,而是用到才加载、不用不加载,节省内存、提升启动速度。

完整生命周期:加载 → 链接 → 初始化 → 使用 → 卸载

阶段详解

  • 加载:读取磁盘 .class 字节码文件,解析为内存数据结构,生成 Class 对象。

  • 链接(分为三步)

    1. 验证:校验字节码格式、安全性、合法性,防止恶意/错误字节码。

    2. 准备:为static 静态变量分配内存并赋默认初始值(0、null、false),仅赋系统默认值。

    3. 解析:将代码中的符号引用统一转换为内存直接引用

  • 初始化:类加载唯一执行业务逻辑的阶段。JVM 主动执行静态代码块、为静态变量赋予代码中自定义初始值,是静态资源真正初始化的阶段。

  • 使用:程序正常调用类的属性、方法、执行业务逻辑。

  • 卸载:类无任何引用、类加载器被回收,释放元空间内存。

1.2 四大类加载器体系

1.2.1 启动类加载器(Bootstrap ClassLoader)

  • 顶层核心加载器,HotSpot 虚拟机中由C++ 编写,Java 代码无法获取其引用,调用获取方法返回null

  • 核心职责:加载 Java 底层核心类库(rt.jar 等基础核心依赖)。

  • 加载特点:按名索骥,非全盘扫描

    • JVM 启动不会遍历加载 lib 目录所有 jar,核心类库范围在 JVM 底层 C++ 源码中硬编码固定
核心安全两道防线(防止核心类篡改)
  1. 字节码校验机制:若直接替换官方 rt.jar,JVM 启动会校验核心类结构、本地方法映射。一旦检测到类结构被篡改、方法缺失,直接抛出SecurityException/LinkageError,拒绝启动。

  2. 双亲委派机制:禁止自定义核心类覆盖原生类。例如自定义java.lang.String无法生效。

双亲委派拦截流程
  1. 程序加载自定义java.lang.String时,应用类加载器优先向上委派;

  2. 依次委派至扩展类加载器、最终抵达启动类加载器;

  3. 启动类加载器检测到核心类库已存在该类,直接返回官方原生 String 类

  4. 自定义核心类彻底失效,永远不会被加载执行。

1.2.2 扩展/平台类加载器(Extension / Platform ClassLoader)

负责加载 Java 拓展类库、系统扩展依赖,承接启动类加载器与应用类加载器的中间委派能力。

1.2.3 应用程序类加载器(Application ClassLoader)

  • 又称系统类加载器,可通过ClassLoader.getSystemClassLoader()获取。

  • 日常开发中,所有自定义类、第三方 Jar 包默认由该加载器加载

1.2.4 自定义类加载器(Custom ClassLoader)

  • 实现方式:开发者继承ClassLoader类,重写findClass()方法。

  • 使用场景:热部署、代码加密、自定义资源加载、特殊业务类加载需求。

1.3 Class 常量池

Java 源码编译为 .class 文件后,文件内部会生成一张常量池表,存储编译期固定数据,是运行时常量池的基础。

存储内容

  • 字面量:字符串字面量、final 修饰常量、基本数据类型固定值。

  • 符号引用:类和接口全限定名、字段名称与描述符、方法名称与描述符。

核心作用

编译期无法确定类、方法、字段的真实内存地址,因此用字符串符号临时占位;在类加载解析阶段,统一替换为真实内存直接引用。

二、执行引擎子系统

2.1 核心职责

将 .class 字节码指令,翻译、优化为操作系统、CPU 可识别的机器指令并执行,是 JVM 真正执行业务逻辑的核心模块。

2.2 执行架构:解释器 + JIT 混合模式

现代 HotSpot JVM 默认采用混合执行模式,兼顾项目快速启动与长期运行高性能。

┌──────────────────────────────────────────────────┐ │ 执行引擎子系统 │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌────────┐ │ │ │ 解释器 │ │ JIT编译器 │ │ 垃圾 │ │ │ │ (Interpreter)│ │ (JIT Compiler)│ │ 收集器 │ │ │ └──────────────┘ └──────────────┘ │ (GC) │ │ │ │ │ └────────┘ │ └───────────┼────────────────┼─────────────────────┘ ▼ ▼ 逐行翻译 热点编译 启动快、执行慢 启动慢、执行极快

两者协作机制

  1. 项目启动初期:解释器逐行解释执行字节码,无需预编译,保证项目秒级启动、快速响应。

  2. 运行过程中:JVM 内置计数器持续探测热点代码(高频循环、频繁调用的代码)。

  3. 热点命中后:JIT 编译器后台将热点字节码编译、优化为本地机器码并缓存。

  4. 无缝替换执行:后续再次调用该代码,直接执行优化后的机器码,放弃逐行解释,大幅提升运行吞吐量。

2.3 GC 模块定位

垃圾回收器(GC)是执行引擎子系统的核心组件,后台静默运行,自动回收堆内存中失效对象,防止内存溢出、内存泄漏。

三、垃圾回收 GC 核心体系

3.1 七大经典垃圾回收器分类

新生代 (Young): Serial ─── Parallel Scavenge ─── ParNew │ │ │ │ │ │ 老年代 (Old): Serial Old ─── Parallel Old ─── CMS 整堆 / 区域化回收: G1 (Garbage First)

3.1.1 串行收集器(Serial + Serial Old)

  • 工作模式:单线程回收,GC 期间全程 STW(暂停所有用户线程)

  • 回收算法:新生代复制算法、老年代标记-整理算法。

  • 适用场景:客户端程序、单核 CPU、极小内存环境(几十MB~两百MB)。

3.1.2 并行吞吐量收集器(Parallel Scavenge + Parallel Old)

  • 定位:JDK8 默认垃圾回收器

  • 工作模式:多线程并行回收,依旧存在 STW,但利用多核 CPU 大幅缩短回收耗时。

  • 核心目标:最大化 CPU 利用率,追求高吞吐量

  • 适用场景:后台批处理、数据计算、无高交互的服务端业务。

3.1.3 并发低延迟收集器(ParNew + CMS)

  • 核心优势:部分 GC 阶段与用户线程并发执行,大幅降低停顿时间。

  • 核心目标:追求低延迟、低停顿

  • 缺点:采用标记-清除算法,产生内存碎片;对 CPU 资源消耗敏感。

  • 版本现状:JDK9 标记废弃,JDK14 彻底移除。

3.2 三色标记算法(并发 GC 核心原理)

三色标记是 CMS、G1、ZGC 等现代并发回收器的核心存活判定算法,用于并发标记阶段精准区分对象存活状态。

[ 黑色 (Black) ] ───> [ 灰色 (Gray) ] ───> [ 白色 (White) ] (自身及下属全完工) (自身完工,下属未遍历完) (未访问 / 垃圾对象)

状态定义

  • 白色:初始状态,所有对象默认白色;标记结束仍为白色则判定为垃圾。

  • 灰色:中间过渡状态,当前对象已扫描,但引用的子对象未全部遍历完成。

  • 黑色:完全存活对象,自身及所有引用子对象全部扫描完毕,安全存活,不会被回收。

3.2.1 正常标记流程

  1. 初始状态:堆内所有对象标记为白色。

  2. 初始标记:从 GC Roots 出发,将直接关联的第一层对象标记为灰色。

  3. 并发标记:遍历灰色对象引用,将白色子对象依次置灰;遍历完成的灰色对象转为黑色。

  4. 最终状态:所有灰色对象全部转正,剩余白色对象统一回收。

3.2.2 并发标记致命问题:对象漏标

单 GC 线程标记时三色标记无问题,但用户线程与 GC 线程并发执行时,会出现漏标问题,导致存活对象被误回收。

漏标场景还原

初始状态:黑色对象 A 引用灰色对象 B,灰色对象 B 引用白色对象 C。

用户线程同时执行两步操作:

  1. B 断开对 C 的引用(B.c = null);

  2. 黑色 A 新建引用指向白色 C(A.c = C)。

【篡改前】 【篡改后】 [ A (黑) ] [ A (黑) ] ──(新引用)──> [ C (白) ] │ ▼ [ B (灰) ] ──> [ C (白) ] [ B (灰) ] (已断开引用)
问题结果

GC 仅扫描灰色对象 B,B 无引用 C,扫描完毕后 B 变为黑色;黑色对象不会二次扫描,导致存活对象 C 始终为白色,最终被当做垃圾回收,引发空指针异常。

结论:CMS 的重新标记、G1 的最终标记,都是为了修复并发漏标问题。

3.2.3 CMS 与 G1 漏标修复机制对比

1. CMS 重新标记(增量更新算法)
  • 核心逻辑:并发期间检测到黑色对象指向白色对象,通过写屏障将黑色对象重置为灰色。

  • 修复阶段:全程 STW,批量重新扫描所有被重置的灰色对象引用链。

  • 缺点:需扫描大量对象,甚至连带扫描新生代,STW 停顿时间长、性能差

2. G1 最终标记(SATB 原始快照算法)
  • 核心逻辑:并发期间检测到灰色对象断开白色对象引用,通过写屏障将旧引用存入 SATB 缓冲区,并提前将对象置灰。

  • 修复阶段:STW 仅处理缓冲区少量漏网数据,无需全量扫描。

  • 优点:停顿时间极短、开销可控、性能稳定。

3.3 GC 分类体系(面试高频)

3.3.1 部分收集(Partial GC)

① 新生代收集(Minor GC / Young GC)
  • 回收范围:仅新生代 Eden、S0、S1 区域。

  • 触发条件:Eden 区内存耗尽。

  • 核心特点:对象朝生夕灭,触发频繁;采用复制算法,速度极快,STW 耗时仅几毫秒至几十毫秒。

② 老年代收集(Major GC / Old GC)
  • 回收范围:仅针对老年代区域。

  • 触发条件:老年代内存空间不足。

  • 特点:速度比 Minor GC 慢 10 倍以上,STW 耗时更长;多数场景会伴随 Full GC。

③ 混合收集(Mixed GC,G1 专属)
  • 回收范围:全部新生代 + 部分垃圾最多的老年代 Region。

  • 触发机制:老年代内存占用达到阈值XX:InitiatingHeapOccupancyPercent

  • 特点:按回收价值筛选区域,精准回收,可控停顿时间,适配大内存服务。

3.3.2 整堆收集(Full GC,最重 GC)

  • 回收范围:新生代、老年代、元空间,整堆全量回收。

  • 四大核心触发条件(必考)

    1. 老年代空间不足,对象晋升失败;

    2. 元空间/方法区加载类过多,内存耗尽;

    3. 代码主动调用System.gc()(建议回收,大概率触发 Full GC);

    4. Minor GC 空间分配担保失败。

  • 核心特点:全局 STW,暂停所有业务线程,服务卡顿严重;线上调优核心目标:减少 Full GC 频次与耗时。

四、运行时数据区子系统

运行时数据区分为线程共享区(全局共用)和线程私有区(线程隔离、随线程生死)两大模块。

4.1 线程共享内存区域

4.1.1 堆 Heap(GC 核心区域)

JVM 最大内存区域,所有对象实例、数组默认在堆内存分配,是 GC 主要管理和回收的区域。

对象完整内存结构
[ 栈 ] [ 堆 (Heap) ] user ──(存储指针)──> ┌──────────────────────────────┐ │ 1. 对象头 (Mark Word + Klass) │ ├──────────────────────────────┤ │ 2. 实例数据 (id, name, age) │ ├──────────────────────────────┤ │ 3. 对齐填充 (补齐为8字节倍数) │ └──────────────────────────────┘
对象创建完整流程
  1. 类加载检查:校验目标类是否已加载,未加载则优先执行类加载流程。

  2. 内存分配:堆内存规整采用「指针碰撞」,内存碎片多采用「空闲列表」。

  3. 并发安全保障(TLAB):为每个线程分配私有缓冲区,多线程创建对象互不抢占、避免并发冲突。

  4. 内存零值初始化:对象内存(不含对象头)默认赋 0、null,保证实例变量可直接访问。

  5. 设置对象头:写入哈希码、GC 分代年龄、锁状态、类指针等核心元数据。

  6. 构造方法初始化:执行<init>方法,按业务代码为属性赋值,完成对象创建。

堆内存分代结构
┌────────────────────────────────────────────────────────────┐ │ 堆内存 (Heap) │ ├─────────────────────────────────────┬──────────────────────┤ │ 新生代 (Young) │ 老年代 (Old) │ ├──────────────────┬──────────┬───────┤ │ │ Eden (伊甸园) │ From (S0)│To (S1)│ 长期存活的老对象 │ └──────────────────┴──────────┴───────┴──────────────────────┘
新生代(对象新手村)
  • Eden 区:绝大多数新对象的诞生、驻留区域。

  • Survivor S0/S1:存活对象中转站。

GC 流转规则:Eden 满触发 Minor GC,存活对象复制至 S0/S1,年龄+1;下次 GC 时,Eden + 上一轮 Survivor 存活对象转移至另一块 Survivor 区,来回流转、年龄递增。

老年代(对象养老院)

存储长期存活、生命周期稳定的对象,三种晋升机制:

  1. 高龄晋升:对象年龄达到阈值(默认15,可通过-XX:MaxTenuringThreshold修改),晋升老年代。

  2. 大对象直接晋升:超大数组、长字符串等大对象,新生代无法容纳,直接分配至老年代,避免频繁复制损耗性能。

  3. 动态年龄判定:Survivor 中同年龄对象总大小超过 Survivor 区域一半,该年龄及以上对象直接晋升老年代。

垃圾存活判定:可达性分析算法

HotSpot 虚拟机核心垃圾判定算法:以GC Roots为起始节点,遍历引用链,不可达对象判定为垃圾。

GC Roots 核心来源
  • 虚拟机栈局部变量表引用的对象(方法内正在使用的局部对象);

  • 方法区静态属性、常量引用的对象;

  • 字符串常量池中的引用对象;

  • 本地方法栈 JNI 引用的 Native 关联对象;

  • JVM 内部常驻对象(Class 对象、系统异常对象、类加载器等)。

4.1.2 元空间 Metaspace(JDK8+)

JDK8 废弃永久代(PermGen),采用元空间,直接使用操作系统本地内存,不占用堆内存

核心存储内容
  • 类元信息:类名、父类、接口、修饰符、类结构描述;

  • 字段、方法信息:名称、类型、参数、修饰符、方法字节码;

  • 运行时常量池、字符串常量池引用;

  • 虚方法表、接口方法表;

  • JIT 编译优化缓存、逃逸分析、计数器数据。

运行时常量池

每个类加载后独立生成,存储编译期字面量、符号引用;在类加载解析阶段,将符号引用动态翻译为内存直接引用。

字符串常量池
  • 位置变迁:JDK7 前在方法区,JDK7 及以后迁移至堆内存

  • 特性:全局共享、自动去重;创建字符串时优先查询常量池,存在则复用引用,不存在则新建对象。

虚方法表(vtable / itable)
  • vtable:存储所有可重写方法的内存地址,支撑多态动态绑定;

  • itable:存储接口方法的具体实现地址;

  • 方法调用时直接查表定位指令入口,执行效率极高。

4.2 线程私有内存区域

线程私有区域随线程创建而生、随线程销毁回收,线程间完全隔离,无并发竞争问题。

4.2.1 程序计数器

  • JVM 最小内存单元,仅存储指令指针地址,唯一无 OOM 的内存区域

  • 执行普通 Java 方法:记录当前执行的字节码指令地址。

  • 执行 Native 方法:计数器值为 Undefined,由操作系统直接执行,不受 JVM 管控。

4.2.2 虚拟机栈(Java 栈)

每个方法执行对应一个栈帧,方法调用、执行、结束对应栈帧入栈、运行、出栈。每个栈帧包含四大核心组件:

1. 局部变量表

编译期确定内存大小,是固定长度数组,存放方法参数、局部变量、基本数据类型、对象引用指针、返回地址。运行期间大小不可变更。

2. 操作数栈

执行引擎的临时计算工作台,基于栈式架构运行。所有加减乘除、赋值运算均通过压栈、弹栈完成,是字节码运算的核心载体。

3. 动态链接

栈帧持有运行时常量池引用,运行期将字节码中的符号引用,动态转换为方法、字段的直接内存引用,支撑多态与动态绑定。

4. 方法返回地址

记录方法调用位置,方法正常 return 退出或异常退出时,恢复上层方法执行上下文,继续执行业务逻辑。

4.2.3 本地方法栈

专门为native本地方法服务。Native 方法由 C/C++ 编写,编译为系统底层动态库,用于操作操作系统内存、硬件、实现高性能底层能力,弥补 Java 底层操作短板。

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

华为eNSP模拟器实战:手把手教你搞定ISIS邻居建立与路由查看

华为eNSP模拟器实战&#xff1a;ISIS邻居建立与路由验证全流程解析 在华为网络设备的学习和认证体系中&#xff0c;eNSP模拟器是不可或缺的实践工具。对于正在备考HCIA或HCIP认证的网络工程师来说&#xff0c;掌握ISIS协议的配置与验证是必备技能。本文将带您深入理解ISIS邻居建…

作者头像 李华
网站建设 2026/6/9 9:50:09

【C#】 ASCII 码转字符串技术解析

【C#】 ASCII 码转字符串技术解析 ASCII&#xff08;American Standard Code for Information Interchange&#xff09;是计算机历史上最基础、最持久的字符编码标准。在 C# 开发中&#xff0c;ASCII 码与字符串的转换看似简单&#xff0c;实则贯穿编码理论、内存模型、跨平台兼…

作者头像 李华
网站建设 2026/6/9 9:47:29

太阳能舆情分析实战:Python+NLP情绪识别与业务落地

1. 项目概述&#xff1a;用自然语言处理读懂公众对太阳能的真实态度“Sentiment Analysis on Solar Energy With NLP And Python”——这个标题乍看像学术论文的副标题&#xff0c;但在我过去八年做能源类数据产品、给光伏企业搭舆情监测系统、帮地方政府评估新能源政策落地反馈…

作者头像 李华
网站建设 2026/6/9 9:47:04

手把手教你用凌顶Edge网关+OPCUA驱动,搞定克劳斯玛菲注塑机数据采集

工业物联网实战&#xff1a;克劳斯玛菲注塑机数据采集全流程解析 在工业4.0浪潮下&#xff0c;注塑机作为塑料制品生产的关键设备&#xff0c;其数据采集的智能化程度直接影响生产效率和质量管理水平。本文将详细介绍如何利用凌顶Edge网关配合OPCUA驱动&#xff0c;实现对克劳斯…

作者头像 李华