前言
本文档为全网最系统化、无删减、纯干货JVM学习手册,严格按照9大模块编写:JVM架构、运行时数据区、类加载、垃圾回收、字节码与执行引擎、实战调优、工具链、全链路监控、OOM排查。全文适配Java8~Java17,覆盖面试、日常开发、线上故障排查、生产调优,无废话、无删减、可直接保存下载长期学习背诵。
模块一:JVM 整体架构与核心模块
1.1 JVM 定义
JVM(Java Virtual Machine,Java虚拟机):一款抽象的、基于栈式的虚拟机,能够执行Java字节码,屏蔽操作系统底层差异,实现Java跨平台特性。JVM并不局限于Java语言,只要编译为Class字节码的语言(Kotlin、Scala、Groovy)均可运行在JVM之上。
1.2 JVM 整体架构分层
JVM自上而下分为四层架构,完整描述代码从编写到运行的全过程:
-
源码层:开发者编写的.java源代码,面向业务逻辑,高级人类可读语言。
-
编译层:借助javac前端编译器,将源码编译为平台无关的.class字节码文件,二进制格式,不可直接运行。
-
运行层:JVM核心运行环境,包含四大核心模块,负责加载、解析、执行字节码。
-
系统层:对接操作系统、CPU、内存、硬件资源,将虚拟机指令翻译为本地硬件指令。
1.3 JVM 四大核心模块(必考)
JVM运行层由四大核心子系统构成,互相配合完成程序运行,是JVM最底层骨架:
1.3.1 类加载子系统
负责读取外部Class字节码文件,加载至JVM内存,生成java.lang.Class对象;完成类的验证、准备、解析、初始化,管控类的整个生命周期。
1.3.2 运行时数据区
JVM内存模型,程序运行时所有数据的存储载体。包含线程私有内存与线程共享内存,是对象、变量、方法执行的内存基础,也是GC、OOM问题的核心区域。
1.3.3 执行引擎
JVM的CPU,负责翻译并执行字节码指令。包含解释器、JIT即时编译器、垃圾回收器,是代码真正执行的核心模块。
1.3.4 本地方法接口(JNI)
打通Java与底层C/C++的桥梁,用于调用操作系统底层资源。Java中标记native的方法均由该模块执行,例如线程、IO、内存操作底层方法。
1.4 跨平台与字节码设计思想
1.4.1 跨平台本质
核心口号:一次编译,到处运行。
原理:Java源码仅编译一次生成统一格式的Class字节码,不同操作系统安装对应适配版本的JVM;由JVM屏蔽Windows、Linux、Mac系统指令差异,统一解析执行字节码。
1.4.2 字节码设计思想
-
中立性:不属于任何操作系统、CPU架构;
-
精简性:指令短小、文件体积小,加载速度快;
-
通用性:多语言编译为字节码,实现语言无关性;
-
可移植性:任意装有JVM的设备均可运行。
1.5 虚拟机架构分类
1.5.1 基于栈式虚拟机(HotSpot JVM)
执行计算依赖虚拟机内部操作数栈,无硬件寄存器绑定。
优点:跨平台极强、指令简单、移植性好;缺点:执行速度略慢。
1.5.2 基于寄存器式虚拟机
模拟物理CPU寄存器,直接在寄存器中运算。
优点:执行效率高、运算速度快;缺点:平台耦合度高、移植差。代表:Lua、ART。
1.5.3 混合架构虚拟机
结合栈式跨平台优势与寄存器高效执行优势,新版ZGC、OpenJDK均采用混合优化架构。
模块二:运行时数据区(堆、栈、方法区完整版解析)
2.1 内存区域整体划分
2.1.1 线程私有内存(生命周期跟随线程、无GC)
线程私有区域,线程创建分配、线程销毁释放,不存在垃圾回收。包含:程序计数器、虚拟机栈、本地方法栈。
2.1.2 线程共享内存(全局共享、GC管控)
进程级别共享,所有线程均可访问,存在GC回收。包含:Java堆、方法区、运行时常量池、直接内存。
2.2 线程私有区域详解
2.2.1 程序计数器(唯一无OOM区域)
-
作用:记录当前线程正在执行的字节码行号;
-
特性:线程私有、内存极小、无GC、永远不会发生内存溢出;
-
使用场景:线程切换后恢复执行位置。
2.2.2 Java虚拟机栈(Java Stack)
-
存储内容:每一个方法调用对应一个栈帧;
-
栈帧组成:局部变量表、操作数栈、动态链接、方法返回地址、附加信息;
-
异常类型:
-
StackOverflowError:栈深度过大(递归死循环);
-
OOM:不断扩容导致栈内存耗尽;
-
-
生命周期:方法调用入栈,方法执行完毕自动出栈销毁。
2.2.3 本地方法栈
专门服务于Native本地方法,执行C/C++底层代码;异常类型、内存结构与虚拟机栈一致。
2.3 线程共享区域详解
2.3.1 Java堆(Heap,GC主战场)
-
存储内容:所有对象实例、数组,唯一存放对象的内存区域;
-
内存分区:新生代 + 老年代;
-
分区比例:Eden:S0:S1 = 8:1:1;
-
区域作用:
-
Eden区:新建对象存放位置;
-
Survivor区:存活对象中转、筛选长期存活对象;
-
老年代:存放存活时间久、大体积对象;
-
-
调控参数:-Xms、-Xmx、-Xmn;
-
溢出异常:Java heap space。
2.3.2 方法区(JDK8永久代→元空间)
-
存储内容:类元信息、常量、静态变量、即时编译代码、方法结构;
-
版本演变:
-
JDK1.6及之前:永久代(堆内内存);
-
JDK1.8:彻底删除永久代,改为元空间Metaspace(本地物理内存);
-
-
溢出场景:大量动态代理、CGLIB生成类、频繁加载Class;
-
异常:Metaspace。
2.3.3 运行时常量池
隶属于方法区,存放编译期生成的字面量、符号引用;运行期间可动态放入常量。
2.3.4 直接内存(堆外内存)
-
不属于JVM运行时数据区,不受堆内存限制;
-
使用场景:NIO、Netty、零拷贝;
-
优点:读写速度快、减少内存拷贝;
-
异常:Direct buffer memory。
2.4 所有内存区域异常汇总表
-
程序计数器:无异常;
-
虚拟机栈:StackOverflowError、栈OOM;
-
堆:Java heap space;
-
元空间:Metaspace;
-
直接内存:Direct buffer memory。
模块三:类加载子系统(5阶段、双亲委派、自定义加载器)
3.1 类完整生命周期(7阶段)
加载 → 验证 → 准备 → 解析 → 初始化 → 使用 → 卸载。
3.2 五大核心加载阶段(重点)
3.2.1 加载阶段
读取磁盘Class二进制文件,加载到内存;在方法区生成Class类对象,保存类结构信息。
3.2.2 验证阶段(安全校验)
四层校验:文件格式验证、元数据验证、字节码验证、符号引用验证;防止恶意字节码破坏虚拟机。
3.2.3 准备阶段
为静态变量 分配内存,赋默认初始值(int=0、boolean=false、引用=null),不赋值开发者自定义值。
3.2.4 解析阶段
将代码中的符号引用 (字符串格式)替换为直接内存引用(真实内存地址),为执行做准备。
3.2.5 初始化阶段(唯一主动触发阶段)
执行静态代码块、给静态变量赋予开发者自定义初始值;触发条件:new对象、访问静态方法/静态变量、反射、子类初始化、启动主类。
3.3 四层类加载器层级
-
启动类加载器(Bootstrap):C++编写,加载JDK核心依赖rt.jar;
-
平台类加载器(Platform):加载系统扩展包;
-
应用类加载器(System/App):加载开发者自定义项目类;
-
自定义类加载器:开发者手动实现,实现个性化加载逻辑。
3.4 双亲委派模型(面试必考)
3.4.1 执行规则
当类加载器收到加载请求,优先向上委托父加载器加载;父加载器无法加载,自身才加载。加载顺序:启动类加载器→平台→应用→自定义。
3.4.2 核心作用
-
安全防护:防止恶意篡改核心类(例如篡改String类);
-
类唯一性:保证全局同一个类只加载一次;
-
层级隔离:划分系统类与自定义类。
3.4.3 破坏双亲委派三大场景
-
SPI机制:JDBC驱动;
-
Tomcat容器:实现Web应用类隔离;
-
热部署、热加载:动态替换Class。
3.5 自定义类加载器
3.5.1 实现方式
继承ClassLoader,重写findClass方法,自定义读取Class字节码逻辑。
3.5.2 实战用途
-
Class文件加密解密;
-
代码热部署、不停机更新;
-
依赖包隔离、避免类冲突。
模块四:垃圾回收机制(算法、收集器、分代策略)
4.1 对象存活判定方式
4.1.1 引用计数法(废弃)
给对象添加引用计数器,引用+1、失效-1;计数器为0判定回收。缺陷:无法解决循环引用,现代JVM废弃。
4.1.2 可达性分析法(HotSpot主流)
以GC Roots为起始节点,向下遍历引用链;不可到达的对象判定为垃圾对象。
4.1.3 可作为GC Roots的对象
-
虚拟机栈中引用的对象;
-
静态变量引用对象;
-
常量引用对象;
-
本地方法栈Native引用对象。
4.2 Java四大引用体系
-
强引用:默认引用,只要引用存在,永远不回收;
-
软引用:内存充足不回收,内存不足强制回收;适用缓存;
-
弱引用:只要发生GC,必定回收;适用临时缓存;
-
虚引用:无引用效果,仅用于监控GC回收状态。
4.3 三大基础垃圾回收算法
4.3.1 标记-清除算法
标记垃圾、统一清除;优点:简单;缺点:产生大量内存碎片、分配效率低。
4.3.2 标记-复制算法
划分同等大小内存,存活对象复制到空闲区域,清空原区域;优点:无内存碎片;缺点:内存直接浪费一半。适用于新生代。
4.3.3 标记-整理算法
标记垃圾、清除后压缩整理内存,存活对象向一端移动;优点:无碎片;缺点:移动成本高。适用于老年代。
4.4 分代收集策略(JVM核心)
-
新生代 :对象朝生夕死、存活率极低 → 标记复制算法;
-
老年代 :对象存活久、存活率高 → 标记清除/标记整理。
4.5 主流垃圾收集器大全(对比+适用场景)
4.5.1 新生代收集器
-
Serial:单线程收集、客户端使用、内存小;
-
ParNew:多线程版本Serial,唯一适配CMS;
-
Parallel Scavenge:高吞吐量、后台任务、大数据使用。
4.5.2 老年代收集器
-
Serial Old:单线程老年代收集;
-
Parallel Old:配合Parallel,高吞吐量;
-
CMS:并发低延迟、互联网Web服务、存在内存碎片。
4.5.3 全堆统一收集器(现代主流)
-
G1:分区收集、可预测停顿、JDK9默认、均衡吞吐量与延迟;
-
ZGC:毫秒级超低延迟、超大堆、JDK11+;
-
Shenandoah:开源低延迟收集器。
4.6 GC分类
-
Minor GC:新生代GC、频率高、速度快;
-
Major GC:老年代GC、速度慢;
-
Full GC:全堆+元空间统一回收、耗时极长、线上严禁频繁触发。
模块五:字节码与执行引擎(结构、指令、JIT优化)
5.1 Class字节码文件结构
Class文件为二进制字节流,固定顺序结构:
-
魔数:固定0xCAFEBABE,标识Class文件;
-
版本号:主次版本,标识JDK编译版本;
-
常量池:占用空间最大,存放字符串、符号引用;
-
访问标识:标识类、修饰符、权限;
-
类索引、父类索引、接口索引;
-
字段表:成员变量、静态变量;
-
方法表:方法字节码、异常、参数;
-
属性表:附加信息,注解、代码行号。
5.2 常用字节码指令
-
栈操作指令:入栈、出栈;
-
运算指令:加减乘除、位运算;
-
跳转指令:if、for、while循环跳转;
-
对象指令:new、instanceof;
-
方法调用指令:invokestatic、invokevirtual。
5.3 JVM三大执行引擎
5.3.1 解释器
逐行翻译字节码、边解释边执行;启动速度快、运行速度慢,适合少量代码。
5.3.2 JIT即时编译器(核心优化)
监测热点代码,将高频执行字节码编译为本地机器码,缓存复用,运行速度极快。
-
C1编译器:客户端编译、快速简单优化;
-
C2编译器:服务端编译、深度复杂优化。
5.3.3 混合执行模式
JVM默认模式:解释执行 + JIT编译结合,兼顾启动速度与运行效率。
5.4 JIT五大核心优化手段
-
逃逸分析:判断对象是否逃出方法作用域;
-
栈上分配:未逃逸对象分配至栈,不占用堆内存;
-
标量替换:拆解对象为基础数据,取消对象创建;
-
方法内联:合并短小方法,减少调用开销;
-
同步消除:去除无竞争锁,优化同步开销。
5.5 JMM Java内存模型
5.5.1 并发三大特性
原子性、可见性、有序性。
5.5.2 volatile关键字
保证可见性、禁止指令重排序;不保证原子性;底层依靠内存屏障实现。
5.5.3 Happens-Before先行发生原则
八大规则,判定多线程内存可见性,无需开发者手动加锁。
模块六:实战调优(参数配置+案例分析)
6.1 核心JVM内存参数
-Xms:堆初始内存 -Xmx:堆最大内存(生产必须 Xms=Xmx) -Xmn:新生代内存大小 -XX:SurvivorRatio:伊甸区比例 -XX:MetaspaceSize:元空间初始值 -XX:MaxMetaspaceSize:元空间上限 -XX:MaxDirectMemorySize:直接内存上限
6.2 主流收集器启用参数
-XX:+UseConcMarkSweepGC 启用CMS -XX:+UseG1GC 启用G1(JDK9默认) -XX:+UseZGC 启用ZGC(JDK11+) -XX:+PrintGCDetails 打印详细GC日志
6.3 调优三大核心目标
-
高吞吐量:大数据、定时任务 → Parallel收集器;
-
低延迟:互联网接口、高并发服务 → G1/ZGC;
-
通用原则:严控FullGC、减少GC停顿、控制内存占用。
6.4 线上实战调优案例
6.4.1 微服务小堆调优
限制堆内存、使用G1、降低元空间大小,减少资源占用。
6.4.2 定时任务频繁GC优化
增大新生代、提高吞吐量、减少对象晋升老年代。
6.4.3 高并发接口卡顿优化
更换ZGC、降低停顿时间、优化内存屏障。
模块七:JVM工具链(命令行+可视化+Arthas+MAT)
7.1 JDK自带命令行工具(必备)
-
jps:查看Java进程、进程ID、启动参数;
-
jstat:实时监控GC次数、占用、类加载、编译统计;
-
jmap:查看堆内存对象分布、dump堆快照;
-
jstack:排查线程死锁、CPU飙高、线程阻塞;
-
jinfo:动态查看、修改虚拟机运行参数;
-
jcmd:全能命令,整合所有工具功能。
7.2 基础可视化工具
-
JConsole:简易监控、免费、基础指标查看;
-
JVisualVM:全能可视化,监控内存、线程、GC、导出快照。
7.3 线上诊断神器:Arthas(阿里)
生产环境无停机、无侵入诊断工具,企业必备。
-
查看方法执行耗时、追踪调用链路;
-
动态查看运行时参数、返回值;
-
热更新代码、无需重启服务;
-
监控线程、内存、GC实时状态。
7.4 内存泄漏专用:MAT
专业分析dump堆快照,定位内存泄漏。
-
统计大对象、冗余对象;
-
分析对象引用链,找出泄漏源头;
-
生成泄漏报表、风险提示。
模块八:性能监控(全链路监控方案)
8.1 核心监控指标
8.1.1 内存指标
堆内存使用率、元空间占用、直接内存占用、内存波动。
8.1.2 GC指标
MinorGC次数、FullGC次数、GC停顿耗时、GC吞吐量。
8.1.3 线程指标
活跃线程、阻塞线程、等待线程、死锁线程、峰值线程数。
8.1.4 类加载指标
加载类数量、卸载类数量、元空间增长速率。
8.2 企业级监控架构
SpringBoot + Actuator + Prometheus + Grafana
-
Actuator:暴露JVM监控端点;
-
Prometheus:时序数据存储、指标采集;
-
Grafana:可视化大屏、曲线图、仪表盘。
8.3 线上告警策略
-
堆内存使用率超过80%告警;
-
1小时出现多次FullGC告警;
-
线程阻塞、死锁告警;
-
元空间持续上涨告警。
模块九:OOM内存溢出(全场景分析+排查流程)
9.1 八大OOM全场景详解
-
Java heap space:堆内存溢出,大量对象、内存泄漏;
-
Metaspace:动态类过多、代理频繁、类未卸载;
-
Direct buffer memory:NIO堆外内存未释放;
-
StackOverflow:递归死循环、方法嵌套过深;
-
无法创建新线程:线程池无限创建、线程无销毁;
-
超大数组OOM:一次性申请超大连续内存;
-
本地内存溢出:系统物理内存耗尽;
-
容器限制OOM:Docker环境内存配额不足。
9.2 高频内存泄漏原因
-
静态集合长期持有引用;
-
线程池线程常驻不销毁;
-
监听器、回调方法未注销;
-
IO流、连接池资源未关闭;
-
缓存无过期淘汰策略。
9.3 线上标准排查流程(企业通用)
-
查看日志,判定OOM异常类型;
-
jstat持续观察内存、GC走势;
-
jstack排查线程死锁、阻塞;
-
jmap导出dump堆快照;
-
MAT分析引用链、定位泄漏对象;
-
代码溯源、修复bug;
-
压力测试、验证修复效果。
附录:JVM高频面试总结
-
简述JVM四大核心模块?
-
运行时数据区划分、各区域作用?
-
双亲委派模型原理、作用、破坏场景?
-
GC可达性分析、GC Roots包含哪些?
-
CMS、G1、ZGC区别与适用场景?
-
JIT优化包含哪些手段?
-
线上OOM排查完整流程?
-
生产环境JVM调优原则?
-
内存泄漏与内存溢出区别?
-
volatile底层原理、内存屏障?