JVM 核心知识点总结

一、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个组件:

  1. 解释器:逐行解析字节码执行,优点是启动快,缺点是执行效率低;
  2. JIT编译器(即时编译器):将热点代码(频繁执行的代码) 一次性编译为机器码,缓存后重复执行,大幅提升执行效率;
  3. 本地方法接口(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方法的执行上下文;
  • 同样会抛出 StackOverflowErrorOutOfMemoryError 异常;
  • 不同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步的类加载过程

核心步骤详解

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

3. 类加载器的分类(4类)

类加载的执行者是类加载器,JVM内置3类核心类加载器,开发者可自定义第4类,按加载优先级从高到低排序:

  1. 启动类加载器(Bootstrap ClassLoader):最顶层,C/C++编写,加载JDK的核心类库(rt.jar),无父类加载器;
  2. 扩展类加载器(Extension ClassLoader):加载JDK扩展类库(jre/lib/ext),父类是启动类加载器;
  3. 应用程序类加载器(Application ClassLoader):加载项目的classpath下的类,是程序默认的类加载器,父类是扩展类加载器;
  4. 自定义类加载器:继承ClassLoader,开发者自定义加载规则,比如加载磁盘/网络中的.class文件。

4. 双亲委派模型

核心定义

JVM类加载的核心机制:一个类加载器收到类加载请求时,首先将请求委托给父类加载器,只有父类加载器无法加载时,子类加载器才会自己加载

核心优点

  1. 保证类的唯一性:同一个类只会被加载一次,避免重复加载;
  2. 保证核心类的安全:核心Java类(如java.lang.String)只能被启动类加载器加载,防止恶意代码篡改核心类;
  3. 实现类的分层加载:核心类库由顶层加载,业务类由应用加载,职责清晰。

五、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的核心基础:

  1. 强引用:最普通的引用(Object obj = new Object()),只要强引用存在,对象永远不会被GC回收,是内存溢出的主要原因;
  2. 软引用:内存充足时不回收,内存不足时才回收,适合做缓存(SoftReference);
  3. 弱引用:只要触发GC,无论内存是否充足,都会回收,适合做临时缓存(WeakReference);
  4. 虚引用:最弱的引用,仅用于监听对象被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种)

  1. Serial GC(串行回收器):单线程回收,暂停所有用户线程(STW),效率低,适合单核、小内存应用;
  2. ParNew GC(并行回收器):Serial的多线程版本,多核环境下效率提升,是CMS回收器的默认新生代搭档。

老年代回收器(5种)

  1. Parallel GC(并行回收器) :JDK8 默认的垃圾回收器 ,新生代+老年代都支持并行回收,追求最大吞吐量,适合后台计算、批量处理等无界面应用;

  2. CMS GC(并发标记清除) :以最短停顿时间为目标,并发执行,减少STW时间,适合响应时间敏感的应用(如电商、金融),缺点是产生内存碎片、CPU占用高;

  3. G1 GC(Garbage First) :JDK9 默认的垃圾回收器,JVM的终极回收器之一,兼顾吞吐量和停顿时间,将堆划分为多个区域,优先回收垃圾多的区域,无内存碎片,适合大内存、高并发应用;

  4. ZGC(Z垃圾回收器) :JDK15正式转正,以极致低延迟(≤10ms) 为核心目标,支持TB级超大内存,通过染色指针读屏障内存多重映射技术实现并发回收,无内存碎片,适合超大内存、核心低延迟业务(如金融高频交易);

  5. 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. 调优核心步骤

  1. 开启GC日志,记录GC的频率、STW时间、内存占用;
  2. 分析日志,判断是内存不足还是GC策略不合理;
  3. 调整堆内存大小、新生代比例、垃圾回收器;
  4. 压测验证,对比调优前后的吞吐量、响应时间;
  5. 迭代优化,直到达到业务要求。

八、JVM 高频考点

✅ 考点1:JVM运行时数据区分为哪些部分?线程私有/共享分别有哪些?

  1. 线程私有:程序计数器、虚拟机栈、本地方法栈;
  2. 线程共享:堆、方法区(元空间)。

✅ 考点2:为什么程序计数器不会发生OOM?

程序计数器是一块极小的、固定的内存空间,存储的内容只有指令地址,内存大小不会增长,因此永远不会溢出。

✅ 考点3:GC的判断对象是否为垃圾的算法是什么?GC Roots包含哪些?

  1. 核心算法:可达性分析算法;
  2. GC Roots:虚拟机栈的局部变量、方法区的静态变量、常量、本地方法栈的native引用。

✅ 考点4:新生代和老年代分别使用什么垃圾回收算法?为什么?

  1. 新生代:标记-复制算法,因为新生代对象存活率低,复制成本低,无内存碎片;
  2. 老年代:标记-整理算法,因为老年代对象存活率高,整理成本低于复制成本,无内存碎片。

✅ 考点5:双亲委派模型的原理和优点是什么?

  1. 原理:子类加载器先委托父类加载器加载类,父类加载失败才自己加载;
  2. 优点:保证类的唯一性、保证核心类的安全、实现类的分层加载。

✅ 考点6:JDK8对JVM的核心变更是什么?

永久代被元空间替代,元空间使用操作系统的本地内存,不再属于堆内存,解决了永久代溢出的问题。

✅ 考点7:JDK8和JDK9的默认垃圾回收器分别是什么?

JDK8默认Parallel GC,JDK9默认G1 GC。

九、核心总结(JVM知识图谱)

  1. JVM是Java的运行核心,核心价值是跨平台+自动内存管理;
  2. 运行时数据区是核心,线程私有无GC,线程共享是GC主战场;
  3. 类加载机制是基础,双亲委派模型保证类的安全和唯一性;
  4. GC是灵魂,可达性分析+分代收集是核心,G1是目前最优的回收器;
  5. 调优是手段,先优化代码再调参数,核心目标是减少GC开销。

JVM的知识点看似繁杂,但所有考点都围绕「内存结构、类加载、GC」三大核心展开。

相关推荐
Maỿbe9 小时前
常见的垃圾收集算法
java·jvm·算法
这周也會开心9 小时前
JVM-G1、老年对象/大对象进入老年代、finalize
jvm
小当家.1059 小时前
JVM八股详解(上部):核心原理与内存管理
java·jvm·学习·面试
曹轲恒10 小时前
方法finalize对垃圾回收器的影响
java·jvm
栗子叶10 小时前
JVM 内存溢出和死锁检测
jvm·调优·死锁
没有bug.的程序员10 小时前
Kubernetes 与微服务的融合架构:调度、弹性、健康检查深度协同
jvm·微服务·云原生·架构·kubernetes·健康检查·弹性伸缩
Chan1621 小时前
【 Java八股文面试 | JavaSE篇 】
java·jvm·spring boot·面试·java-ee·八股
-西门吹雪1 天前
c++线程之std::async浅析
java·jvm·c++
alonewolf_991 天前
深入解析G1与ZGC垃圾收集器:原理、调优与选型指南
java·jvm·算法