一、JVM 核心定位与概述
1. JVM 全称与定义
JVM = Java Virtual Machine ,Java虚拟机 ,是运行所有Java程序的核心运行环境 ,是Java语言的基石。
JVM本质是一个软件模拟的计算机 ,有自己的指令集、内存区域、寄存器等,能识别并执行Java编译后的字节码文件(.class)。
2. JVM 核心使命(Java的核心优势)
实现 Java 的 一次编写,到处运行 (Write Once, Run Anywhere) 跨平台特性:
- Java源码(
.java)编译为与平台无关的字节码(.class),不直接编译为操作系统的机器码; - 不同操作系统(Windows/Linux/Mac)安装对应版本的JVM,由JVM负责将字节码解析/编译为当前系统的机器码执行;
- 程序员无需关注底层系统差异,JVM屏蔽了所有平台相关的细节。
3. JVM 核心地位
Java 程序运行链路:Java源码(.java) → 编译器 → 字节码(.class) → JVM → 机器码 → CPU执行
✅ 结论:JVM是连接Java字节码与操作系统的桥梁,没有JVM,Java程序无法运行。
4. JVM 核心特性
- 跨平台:一套字节码,所有平台的JVM均可执行;
- 自动内存管理:核心的垃圾回收(GC) 机制,无需程序员手动申请/释放内存;
- 面向对象支持:完美实现Java的封装、继承、多态等特性;
- 安全可靠:提供类加载校验、内存沙箱等安全机制,防止恶意代码执行。
二、JVM 整体架构
JVM是一个完整的执行体系,整体分为三大核心模块,执行流程自上而下,缺一不可:

✅ 模块一:类加载子系统
负责将磁盘中的 .class 字节码文件,加载到JVM的内存中(方法区),并完成加载、验证、准备、解析、初始化 5个核心步骤,最终生成可执行的Java类对象。
✅ 模块二:运行时数据区(内存结构)
JVM的核心内存划分,所有加载的类信息、程序运行的变量、线程数据都存储在这里。
《Java虚拟机规范》强制划分的内存区域,下文单独详细讲解。
✅ 模块三:执行引擎
负责执行加载后的字节码指令,核心包含3个组件:
- 解释器:逐行解析字节码执行,优点是启动快,缺点是执行效率低;
- JIT编译器(即时编译器):将热点代码(频繁执行的代码) 一次性编译为机器码,缓存后重复执行,大幅提升执行效率;
- 本地方法接口(Native Method Interface):调用C/C++编写的本地方法库(.dll/.so),执行native修饰的方法。
补充:JVM的执行策略是「解释执行 + 编译执行」混合模式,兼顾启动速度和运行效率。
三、JVM 运行时数据区
核心前置原则
JVM的运行时数据区,按线程归属 分为两大核心类别,这个分类是所有考点的基础,必须牢记:
✅ 线程私有区域 :每个线程独立拥有一份,生命周期与线程一致,线程创建时分配,线程销毁时释放,无GC回收 ;
✅ 线程共享区域 :所有线程共用一份,生命周期与JVM一致,只有在JVM停止时才释放,是垃圾回收(GC)的核心区域。

🔴 第一类:线程私有内存区域(3个)
1. 程序计数器 (Program Counter Register)
核心定义
一块极小的内存空间,也叫PC寄存器,JVM规范中唯一不会抛出内存溢出(OutOfMemoryError)的区域。
核心作用
- 存储当前线程执行Java方法时,下一条要执行的字节码指令地址/行号索引;
- 执行native本地方法时,值为 Undefined(未定义);
- 支撑多线程抢占式调度:线程切换时保存执行位置,恢复时继续执行,实现「断点续跑」;
- 记录方法调用的返回地址,保证方法嵌套调用后正确返回。
核心特性
线程私有、无GC、无OOM、读写效率极致(寄存器级别)。
2. 虚拟机栈 (Java Virtual Machine Stack)
核心定义
每个线程创建时都会分配一个虚拟机栈,栈内存储的核心单元是 栈帧(Stack Frame) ,一个栈帧对应一个Java方法的一次调用过程 。
方法调用时,栈帧入栈;方法执行完毕,栈帧出栈,栈帧的生命周期与方法调用一致。
栈帧核心组成
每个栈帧包含:局部变量表 、操作数栈 、动态链接 、方法返回地址,存储了方法执行的所有上下文信息。
核心特性
- 线程私有,栈的深度由JVM参数控制;
- 无GC回收,栈帧随方法调用自动创建/销毁;
常见异常
- StackOverflowError:栈深度溢出,比如无限递归调用方法,栈帧不断入栈,超出栈的最大深度;
- OutOfMemoryError:虚拟机栈可以动态扩展,当扩展时申请不到足够内存,抛出该异常。
3. 本地方法栈 (Native Method Stack)
核心定义
与虚拟机栈功能完全一致,唯一区别是:虚拟机栈服务于Java方法,本地方法栈服务于native本地方法。
核心特性
- 线程私有,存储native方法的执行上下文;
- 同样会抛出
StackOverflowError和OutOfMemoryError异常; - 不同JVM对本地方法栈的实现不同,比如HotSpot虚拟机直接将本地方法栈和虚拟机栈合二为一。
🔴 第二类:线程共享内存区域(2个 垃圾回收核心区)
1. 堆 (Heap)
核心定义
JVM启动时创建,是JVM中内存占比最大、最核心的区域 ,也是垃圾回收(GC)的主战场,几乎所有的对象实例和数组都在堆中分配内存。
核心地位
- 堆是Java程序运行的核心内存区,所有线程共享,也是内存溢出最常见的区域;
- 堆的大小可以通过JVM参数动态调整,是JVM调优的核心目标。
堆的内存细分
JVM的堆内存采用 分代收集思想 划分,核心依据是:对象的存活周期不同,垃圾回收的策略不同,分为两大区域,细分3块:

✅ 新生代 (Young Generation) :占堆内存的1/3左右,存储新创建的对象、存活周期短的对象
- 新生代内部再分:Eden区(伊甸园) + 两个Survivor区(S0区、S1区,也叫From区、To区),默认比例
8:1:1; - 核心特点:对象创建和销毁频繁,垃圾回收频率高,回收速度快,采用「复制算法」
✅ 老年代 (Old Generation) :占堆内存的2/3左右,存储存活周期长的对象
- 核心特点:对象经过新生代多次GC后仍存活,会被晋升到老年代;回收频率低,单次回收耗时久,采用「标记-整理算法」。
堆的核心异常
OutOfMemoryError: Java heap space → 堆内存溢出,创建的对象过多,内存不足,GC无法回收足够内存。
核心补充
堆内存中还有一个 永久代(PermGen) ,JDK8之前归属于堆,JDK8及以后被 元空间(Metaspace) 替代,永久代彻底移除。
2. 方法区 (Method Area)
核心定义
线程共享的内存区域,也叫「永久代」(JDK8前),存储JVM加载的类的元数据信息,是Java类的「说明书」。
存储内容
- 类的全限定名、访问修饰符、字段信息、方法信息;
- 常量池、静态变量、即时编译器编译后的代码缓存;
- 父类和接口的引用、方法字节码等。
核心特性
- 线程共享,生命周期与JVM一致;
- 方法区的内存也会被GC回收,回收内容是:无用的类信息、常量池中的废弃常量;
永久代 vs 元空间(JDK8重大变更)
✅ JDK7及之前:方法区的实现是 永久代(PermGen) ,属于堆内存的一部分,大小固定,容易溢出;
✅ JDK8及之后:永久代被 元空间(Metaspace) 彻底替代,元空间不再属于堆内存,而是直接使用操作系统的本地内存 ;
✅ 变更原因:永久代大小固定,容易触发 OutOfMemoryError: PermGen space,元空间使用本地内存,内存上限更高,溢出概率大幅降低。
方法区核心异常
- JDK7及之前:
OutOfMemoryError: PermGen space永久代溢出; - JDK8及之后:
OutOfMemoryError: Metaspace元空间溢出。
✔️ 延伸:运行时常量池 (Runtime Constant Pool)
属于方法区的一部分,是.class文件中「常量池」的运行时版本,存储字符串常量、字面量、符号引用等,是String的intern()方法核心依赖,也是方法区的重要组成部分。
四、JVM 类加载机制
1. 类加载的核心定义
类加载子系统将磁盘中的 .class 字节码文件,加载到JVM的方法区,并在堆中生成对应的java.lang.Class对象,这个过程就是类加载,只加载一次,复用永久。
2. 类的完整生命周期(7步)
一个Java类从被加载到JVM内存,到最终被卸载,完整生命周期分为7个步骤,顺序固定,核心是前5步的类加载过程 :

核心步骤详解
- 加载:通过类的全限定名,读取.class文件到方法区,生成Class对象;
- 验证:校验.class文件的合法性,防止恶意字节码、文件损坏,保证安全;
- 准备:为类的静态变量 分配内存,并赋上默认初始值(如int=0、String=null);
- 解析:将常量池中的符号引用 替换为直接引用(内存地址);
- 初始化:执行静态代码块、为静态变量赋程序员指定的初始值,是类加载的核心步骤;
- 使用:程序调用类的方法、访问字段,正常使用类;
- 卸载:类的Class对象被GC回收,类的元数据信息从方法区移除,只有极少场景会触发。
3. 类加载器的分类(4类)

类加载的执行者是类加载器,JVM内置3类核心类加载器,开发者可自定义第4类,按加载优先级从高到低排序:
- 启动类加载器(Bootstrap ClassLoader):最顶层,C/C++编写,加载JDK的核心类库(rt.jar),无父类加载器;
- 扩展类加载器(Extension ClassLoader):加载JDK扩展类库(jre/lib/ext),父类是启动类加载器;
- 应用程序类加载器(Application ClassLoader):加载项目的classpath下的类,是程序默认的类加载器,父类是扩展类加载器;
- 自定义类加载器:继承ClassLoader,开发者自定义加载规则,比如加载磁盘/网络中的.class文件。
4. 双亲委派模型
核心定义
JVM类加载的核心机制:一个类加载器收到类加载请求时,首先将请求委托给父类加载器,只有父类加载器无法加载时,子类加载器才会自己加载。
核心优点
- 保证类的唯一性:同一个类只会被加载一次,避免重复加载;
- 保证核心类的安全:核心Java类(如java.lang.String)只能被启动类加载器加载,防止恶意代码篡改核心类;
- 实现类的分层加载:核心类库由顶层加载,业务类由应用加载,职责清晰。
五、JVM 垃圾回收 GC(Garbage Collection) 核心全解
核心前置说明
GC是JVM的灵魂特性 ,也是Java的核心优势:自动回收不再使用的对象内存,无需程序员手动释放 ,彻底解决了C/C++的内存泄漏问题。
GC的核心回收区域:堆内存 + 方法区 ,线程私有区域无GC回收;GC的核心对象:堆中的对象实例。
✅ 1. 垃圾回收的核心前提:如何判断对象是「垃圾」?
GC的第一步:识别堆中哪些对象是「存活」的,哪些是「死亡(垃圾)」的,JVM有2个核心算法,目前主流是第2个:
① 引用计数法
- 原理:给每个对象加一个引用计数器,被引用时+1,引用失效时-1,计数器为0则是垃圾;
- 缺点:无法解决循环引用问题(A引用B,B引用A,计数器都不为0,永远无法回收),JVM已弃用。
② 可达性分析算法
- 原理:以 GC Roots(GC根节点) 为起点,向下遍历对象的引用链,没有被引用链连接的对象,就是垃圾对象;
- GC Roots 包含哪些对象:虚拟机栈的栈帧中的局部变量 、方法区的静态变量 、方法区的常量 、本地方法栈的native引用。
- 优点:完美解决循环引用问题,是所有JVM的默认垃圾判断算法。
✅ 2. 4种引用类型(按优先级从高到低)
Java的引用类型决定了对象的回收优先级,是GC的核心基础:
- 强引用:最普通的引用(Object obj = new Object()),只要强引用存在,对象永远不会被GC回收,是内存溢出的主要原因;
- 软引用:内存充足时不回收,内存不足时才回收,适合做缓存(SoftReference);
- 弱引用:只要触发GC,无论内存是否充足,都会回收,适合做临时缓存(WeakReference);
- 虚引用:最弱的引用,仅用于监听对象被GC回收的事件,无实际引用意义(PhantomReference)。
✅ 3. 经典垃圾回收算法(4种)
GC的核心算法,JVM的垃圾回收器都是基于这些算法实现的,按使用场景分类,分代收集的核心依据:
① 标记-清除算法 (Mark-Sweep)
- 原理:分两步 → 标记:标记所有存活对象;清除:回收未标记的垃圾对象;
- 优点:实现简单;
- 缺点:产生内存碎片,内存碎片过多会导致无法分配大对象,触发频繁GC。
② 标记-复制算法 (Mark-Copy)
- 原理:将内存分为大小相等的两块,只使用一块;GC时标记存活对象,复制到另一块内存,然后清空当前块;
- 优点:无内存碎片,回收效率高;
- 缺点:内存利用率低(仅50%);
- 适用场景:新生代(对象存活率低,复制成本低),也是新生代的默认算法。
③ 标记-整理算法 (Mark-Compact)
- 原理:分三步 → 标记:标记存活对象;整理:将存活对象向内存一端移动;清除:清空另一端的垃圾对象;
- 优点:无内存碎片,内存利用率100%;
- 缺点:整理阶段需要移动对象,耗时较长;
- 适用场景:老年代(对象存活率高,整理成本低于复制成本),也是老年代的默认算法。
④ 分代收集算法 (Generational Collection)
- 原理:不是独立算法,是组合策略 → 新生代用「标记-复制」,老年代用「标记-整理/标记-清除」;
- 核心依据:新生代对象存活短、回收频繁,复制算法效率高;老年代对象存活长、回收少,整理算法更合适;
- 地位:所有JVM的默认GC策略,是目前最优的垃圾回收思路。
✅ 4. 常用垃圾回收器(5种)
垃圾回收器是GC算法的具体实现,不同回收器有不同的特性、适用场景,JVM支持组合使用,核心分类:新生代回收器 + 老年代回收器,以下是主流的5种,按版本排序:
新生代回收器(2种)
- Serial GC(串行回收器):单线程回收,暂停所有用户线程(STW),效率低,适合单核、小内存应用;
- ParNew GC(并行回收器):Serial的多线程版本,多核环境下效率提升,是CMS回收器的默认新生代搭档。
老年代回收器(5种)
-
Parallel GC(并行回收器) :JDK8 默认的垃圾回收器 ,新生代+老年代都支持并行回收,追求最大吞吐量,适合后台计算、批量处理等无界面应用;
-
CMS GC(并发标记清除) :以最短停顿时间为目标,并发执行,减少STW时间,适合响应时间敏感的应用(如电商、金融),缺点是产生内存碎片、CPU占用高;
-
G1 GC(Garbage First) :JDK9 默认的垃圾回收器,JVM的终极回收器之一,兼顾吞吐量和停顿时间,将堆划分为多个区域,优先回收垃圾多的区域,无内存碎片,适合大内存、高并发应用;
-
ZGC(Z垃圾回收器) :JDK15正式转正,以极致低延迟(≤10ms) 为核心目标,支持TB级超大内存,通过染色指针 、读屏障 和内存多重映射技术实现并发回收,无内存碎片,适合超大内存、核心低延迟业务(如金融高频交易);
-
Shenandoah GC(神龙回收器) :JDK17正式转正,以极致低延迟为核心目标,对标ZGC,无着色指针设计兼容性更好,CPU开销更低,支持TB级内存,无内存碎片,适合超大内存低延迟业务(ZGC的优秀替代方案);
核心补充:STW = Stop The World,GC时暂停所有用户线程,是GC的核心性能指标,STW时间越短,应用的响应性越好。
六、JVM 常见内存溢出(OOM)异常汇总
内存溢出(OutOfMemoryError,简称OOM)是JVM最常见的故障,指JVM申请不到足够的内存空间,分类型整理,含原因+解决方案:
✅ 1. OOM: Java heap space → 堆内存溢出
- 原因:创建的对象过多、对象存活时间过长,堆内存不足,GC无法回收;
- 解决方案:调大堆内存参数(-Xms初始堆内存、-Xmx最大堆内存),排查内存泄漏、减少大对象创建。
✅ 2. OOM: StackOverflowError → 虚拟机栈溢出
- 原因:方法递归调用过深、栈帧过多,超出虚拟机栈的最大深度;
- 解决方案:优化递归逻辑,调大栈内存参数(-Xss)。
✅ 3. OOM: PermGen space → 永久代溢出(JDK7及之前)
- 原因:加载的类过多、常量池过大,永久代内存不足;
- 解决方案:调大永久代参数(-XX:PermSize、-XX:MaxPermSize)。
✅ 4. OOM: Metaspace → 元空间溢出(JDK8及之后)
- 原因:加载的类过多、元数据信息过大,元空间使用的本地内存不足;
- 解决方案:调大元空间参数(-XX:MetaspaceSize、-XX:MaxMetaspaceSize)。
✅ 5. OOM: Direct buffer memory → 直接内存溢出
- 原因:使用NIO的DirectByteBuffer申请的直接内存过多,超出限制;
- 解决方案:调大直接内存参数(-XX:MaxDirectMemorySize)。
七、JVM 性能调优核心
1. 调优核心原则
- 调优是最后手段:先优化代码、解决内存泄漏、减少大对象,再进行JVM调优;
- 调优核心目标:减少GC频率、缩短STW时间、提升吞吐量/响应时间;
- 分代调优:新生代调优为主,老年代调优为辅,新生代的GC频率远高于老年代。
2. 常用核心JVM启动参数
所有参数都是在Java启动时指定,是调优的核心工具,按功能分类:

✔️ 堆内存参数(最核心)
-Xms2G:设置堆的初始内存为2G,建议与-Xmx一致,避免内存扩容的开销;-Xmx4G:设置堆的最大内存为4G;-XX:NewRatio=2:新生代:老年代 = 1:2,默认比例;-XX:SurvivorRatio=8:新生代中Eden:S0:S1 = 8:1:1,默认比例。
✔️ 垃圾回收器参数
-XX:+UseParallelGC:使用Parallel并行回收器(JDK8默认);-XX:+UseG1GC:使用G1回收器(JDK9默认,推荐);-XX:+UseZGC:使用ZGC回收器(JDK15+支持,需手动开启);-XX:+UseShenandoahGC:使用Shenandoah回收器(JDK17+支持,需手动开启);-XX:+PrintGCDetails:打印详细GC日志,排查GC问题必备;-XX:+HeapDumpOnOutOfMemoryError:开启内存溢出时自动保留dump文件(核心参数);-XX:HeapDumpPath=/path/to/dumpfile.hprof:指定内存溢出dump文件的保存路径(可选,默认生成在JVM启动目录);
✔️ 其他核心参数
-Xss1M:设置每个线程的虚拟机栈内存为1M;-XX:MetaspaceSize=256M:设置元空间初始内存;
3. 调优核心步骤
- 开启GC日志,记录GC的频率、STW时间、内存占用;
- 分析日志,判断是内存不足还是GC策略不合理;
- 调整堆内存大小、新生代比例、垃圾回收器;
- 压测验证,对比调优前后的吞吐量、响应时间;
- 迭代优化,直到达到业务要求。
八、JVM 高频考点
✅ 考点1:JVM运行时数据区分为哪些部分?线程私有/共享分别有哪些?
- 线程私有:程序计数器、虚拟机栈、本地方法栈;
- 线程共享:堆、方法区(元空间)。
✅ 考点2:为什么程序计数器不会发生OOM?
程序计数器是一块极小的、固定的内存空间,存储的内容只有指令地址,内存大小不会增长,因此永远不会溢出。
✅ 考点3:GC的判断对象是否为垃圾的算法是什么?GC Roots包含哪些?
- 核心算法:可达性分析算法;
- GC Roots:虚拟机栈的局部变量、方法区的静态变量、常量、本地方法栈的native引用。
✅ 考点4:新生代和老年代分别使用什么垃圾回收算法?为什么?
- 新生代:标记-复制算法,因为新生代对象存活率低,复制成本低,无内存碎片;
- 老年代:标记-整理算法,因为老年代对象存活率高,整理成本低于复制成本,无内存碎片。
✅ 考点5:双亲委派模型的原理和优点是什么?
- 原理:子类加载器先委托父类加载器加载类,父类加载失败才自己加载;
- 优点:保证类的唯一性、保证核心类的安全、实现类的分层加载。
✅ 考点6:JDK8对JVM的核心变更是什么?
永久代被元空间替代,元空间使用操作系统的本地内存,不再属于堆内存,解决了永久代溢出的问题。
✅ 考点7:JDK8和JDK9的默认垃圾回收器分别是什么?
JDK8默认Parallel GC,JDK9默认G1 GC。
九、核心总结(JVM知识图谱)
- JVM是Java的运行核心,核心价值是跨平台+自动内存管理;
- 运行时数据区是核心,线程私有无GC,线程共享是GC主战场;
- 类加载机制是基础,双亲委派模型保证类的安全和唯一性;
- GC是灵魂,可达性分析+分代收集是核心,G1是目前最优的回收器;
- 调优是手段,先优化代码再调参数,核心目标是减少GC开销。
JVM的知识点看似繁杂,但所有考点都围绕「内存结构、类加载、GC」三大核心展开。