Java JVM虚拟机核心知识点总结(架构师_高级工程师必备)

深入浅出Java虚拟机(JVM)核心原理 比如,它会自动帮我们分配内存来存储程序运行时产生的数据,当这些数据不再使用时,JVM 还会自动把它们占用的内存回收掉,这样就不用担心内存不够用或者内存泄漏的问题。(3)虚拟机主要的 5 大块:方法区,堆都为线程共享区域,有线程安全问题,栈和本地方法栈和计数器都是独享区域,不存在线程安全问题,而 JVM 的调优主要就是围绕堆,栈两大块进行。JVM 就负责运行这些字节码文件,它会把字节码翻译成计算机硬件能懂的指令,让程序在不同的操作系统上都能顺利运行。的缩写,简单来说,JVM 就像是一台。 阅读详情

JVM(Java虚拟机)是Java跨平台特性的核心,也是面试和实际开发中考察深度的重点模块。其核心知识点围绕“内存管理”“类加载”“多线程安全”三大方向展开,具体可分为四大核心模块:JVM内存结构(物理基础)、类加载机制(资源加载)、JVM内存模型(多线程安全)、垃圾回收机制(内存高效利用)。四大模块层层递进,共同支撑Java程序的稳定、高效运行,以下结合详细理解和实际场景,全面梳理所有核心细节。

一、JVM内存结构(内存管理的物理基础)

JVM内存结构是JVM管理内存的物理基础,核心是“按对象生命周期和用途划分不同区域”,实现内存隔离和高效管理,从根本上为避免内存泄漏、OOM(OutOfMemoryError)提供支撑。JVM规范将内存划分为多个逻辑区域,各区域功能、生命周期不同,管理策略也不同,具体如下:

1.1 核心内存区域划分及功能

JVM内存区域主要包括堆、方法区、虚拟机栈、本地方法栈、程序计数器,其中堆和方法区是内存管理和垃圾回收的核心关注区域,虚拟机栈和本地方法栈主要与方法调用相关。

(1)堆内存

  • 功能:存放Java程序中所有对象实例和数组,是JVM内存中最大的区域,也是垃圾回收的主要区域(GC的核心战场)。

  • 特点:线程共享,所有线程都能访问堆中的对象;对象生命周期长短不一,既有短期存活的临时对象,也有长期存活的核心对象。

  • 分代划分:为了提高垃圾回收效率,堆内存进一步划分为新生代和老年代,新生代又分为Eden区和两个Survivor区(From Survivor、To Survivor),默认比例为8:1:1。

  • OOM场景:堆内存OOM是最常见的场景,通常是因为创建大量对象且无法被垃圾回收(如无限循环创建对象),导致堆内存被占满。

(2)方法区

  • 功能:存放类信息(类的全限定名、方法信息、字段信息)、常量、静态变量、即时编译后的代码等。

  • 版本变化:JDK8之前,方法区被称为“永久代”,是堆内存的一部分;JDK8及之后,永久代被移除,替换为“元空间”,元空间不再属于堆内存,而是使用本地内存。

  • 优势变化:元空间使用本地内存,大小主要受系统可用内存限制,默认情况下不易出现OOM;但如果动态生成类过多(如频繁反射、动态代理),元空间也可能溢出,可通过JVM参数调整元空间大小。

  • OOM场景:方法区(元空间)OOM常见于加载过多类、常量池溢出,或动态生成类未及时卸载。

(3)虚拟机栈

  • 功能:线程私有,每个线程创建时会对应一个虚拟机栈,栈中存放“栈帧”,每个栈帧对应一次方法调用,存储方法的局部变量、方法参数、返回地址等。

  • 特点:栈帧随方法调用创建,方法执行结束后栈帧自动销毁,生命周期与方法一致,无需复杂的垃圾回收,直接弹出销毁即可。

  • 异常场景:如果递归调用过深,或线程请求栈深度超过JVM允许的最大值,会抛出StackOverflowError(栈溢出),注意这与OOM(OutOfMemoryError)是不同的错误类型。

(4)本地方法栈

  • 功能:与虚拟机栈类似,区别是虚拟机栈服务于Java方法,本地方法栈服务于本地方法(如Native方法)。

  • 异常场景:同样可能抛出StackOverflowError和OutOfMemoryError。

(5)程序计数器

  • 功能:线程私有,用于记录当前线程执行的字节码指令地址,是JVM中唯一不会抛出OOM的区域。

  • 作用:多线程切换时,通过程序计数器记录当前线程的执行位置,切换后能恢复到正确的执行状态。

1.2 内存结构的核心作用

按对象生命周期和用途划分内存区域,核心目的是“高效管理内存”:

  1. 隔离不同生命周期的对象:如堆内存存放长期存活的对象,适合GC重点回收;虚拟机栈的栈帧生命周期短,直接弹出销毁,无需复杂回收。

  2. 针对性管理:不同区域采用不同的管理策略,如堆内存用分代回收,栈直接销毁,提升整体内存管理效率,减少内存浪费。

  3. 避免内存问题:通过明确各区域的功能和边界,便于定位OOM、内存泄漏等问题(如堆溢出和方法区溢出的排查方向不同)。

1.3 补充:栈溢出的解决方式

栈溢出(StackOverflowError)的核心原因是“方法嵌套调用过深,导致栈帧堆积超过栈容量”,解决方式主要有两种:

  1. 优化递归逻辑:减少递归深度,避免无限递归。

  2. 改用迭代方式:迭代是在一个方法内循环执行,全程只创建一个栈帧,不会持续创建新栈帧,从而避免栈溢出。

示例:计算n的阶乘

  • 递归方式(易栈溢出):f(n) = n * f(n-1),每次调用都会压栈,递归深度过深会导致栈帧堆积。

  • 迭代方式(避免栈溢出):int result = 1; for (int i=1; i<=n; i++){ result *=i; },全程只有一个栈帧。

注意:迭代器模式中的迭代(如集合遍历)也属于迭代方式,通过hasNext()和next()在循环中遍历元素,不会嵌套创建新栈帧,避免栈溢出。

二、JVM类加载机制(资源安全加载的核心)

类加载机制是JVM将.class文件(磁盘上的字节码文件)加载到内存(方法区),并转化为可执行代码的完整流程,核心目的是“安全、准确、高效地将类加载到内存,为程序执行提供原料”。类加载机制的核心是双亲委派模型,同时也支持打破双亲委派以满足特殊场景需求。

2.1 类加载的完整流程(加载→验证→准备→解析→初始化→使用→卸载)

类加载的全过程分为7个阶段,其中“加载、验证、准备、解析、初始化”是核心阶段,按顺序执行,可理解为“加载→预处理→初始化→使用→卸载”的逻辑链:

(1)加载(Loading)

  • 核心操作:将磁盘上的.class文件读入内存(方法区),并在堆内存中创建一个java.lang.Class对象,用于封装类的信息,作为程序访问该类的入口。

  • 关键:加载阶段只负责读取文件,不做任何验证和初始化操作,仅完成“文件读取→内存存储”的过程。

(2)验证(Verification)

  • 核心操作:验证.class文件的合法性和安全性,避免恶意代码注入,确保加载的类符合JVM规范。

  • 验证内容:包括文件格式验证(如是否符合class文件格式规范)、语义验证(如类是否有父类、是否符合继承规则)、字节码验证(如是否有非法指令)等。

  • 意义:验证是类加载的“安全校验”步骤,只有验证通过的类,才能进入后续阶段,保障JVM运行安全。

(3)准备(Preparation)

  • 核心操作:为类的静态变量(static变量)分配内存,并设置默认初始值(而非用户指定的初始值)。

  • 示例:static int a = 10; 准备阶段会为a分配内存,设置默认值0,而非10;10的赋值会在初始化阶段完成。

  • 注意:实例变量(非static变量)不会在准备阶段分配内存,其内存分配在对象创建时(堆内存中)完成。

(4)解析(Resolution)

  • 核心操作:将类中的“符号引用”转换为“直接引用”。

  • 通俗理解:符号引用是.class文件中用字符串表示的引用(如“com.xxx.ClassA”),直接引用是内存中的实际地址;解析阶段就是将字符串引用替换为内存地址,让JVM能直接找到对应的类、方法或字段。

(5)初始化(Initialization)

  • 核心操作:执行类的静态代码块,为静态变量赋用户指定的初始值,这是类加载过程中“真正执行代码”的阶段。

  • 触发时机:初始化采用“懒加载”机制,并非程序启动或类加载时立即执行,仅当类被“主动使用”时才会触发:

  1. 创建类的实例(new对象);

  2. 调用类的静态方法;

  3. 访问类的静态变量(读取或修改);

  4. 通过反射调用类(如Class.forName());

  5. 初始化子类时,父类会先初始化。

  • 注意:被动引用不会触发初始化,如“通过子类引用父类的静态变量”,只会触发父类的初始化,不会触发子类的初始化。

(6)使用(Using)

  • 核心操作:类初始化完成后,即可创建实例、调用方法、访问字段,真正投入使用。

(7)卸载(Unloading)

  • 核心操作:当类不再被引用(如类加载器被销毁、所有实例被回收),JVM会将该类的信息从方法区中卸载,释放内存。

  • 注意:JVM核心类(如java.lang.String)通常不会被卸载,因为它们被启动类加载器加载,始终有引用。

2.2 JVM判断类唯一性的标准

JVM判断两个类是否为同一个类,核心依据是“类的全限定名 + 加载该类的类加载器实例”,二者缺一不可:

  1. 即使两个类的全限定名完全相同(如com.xxx.User),若由不同的类加载器实例加载,JVM会视为两个不同的类,可独立共存,不会出现冲突。

  2. 若由同一个类加载器实例加载,全限定名相同的类,只会被加载一次,避免重复加载。

2.3 双亲委派机制(类加载的默认规则)

(1)核心原理

双亲委派机制的核心是“向上委托优先”:当一个类加载器需要加载某个类时,不会先自己尝试加载,而是首先委托给其父加载器,依次向上委托,直到最顶层的启动类加载器;只有当父加载器无法找到该类(即在其搜索路径中找不到对应的.class文件)时,子加载器才会自己尝试加载该类。

JVM默认的类加载器层级(从顶层到下层):启动类加载器(Bootstrap ClassLoader)→ 扩展类加载器(Extension ClassLoader)→ 应用类加载器(Application ClassLoader)→ 自定义类加载器。

(2)核心作用

  1. 保证类的默认唯一性:对于全限定名相同的类,由于父加载器优先加载,子加载器不会重复加载,避免了同一类被多个加载器重复加载导致的内存溢出。

  2. 保证核心类安全:Java核心类(如java.lang.String、java.lang.Object)由启动类加载器加载,子加载器不会自己加载核心类,避免恶意类篡改核心类,保障JVM运行安全。

  3. 简化类加载流程:通过层级委托,统一由父加载器负责加载公共类、核心类,子加载器仅负责加载自身范围内的特殊类,提升加载效率。

2.4 打破双亲委派机制(特殊场景需求)

(1)打破的核心逻辑

打破双亲委派的核心是“反转加载顺序”:子加载器不再优先向上委托父加载器,而是先自己尝试加载自身负责范围内的类(如指定路径下的外部jar包),只有当自己加载失败时,才会委托父加载器加载。

实现手段:通常是重写类加载器的loadClass()方法,调整委托顺序,让子加载器优先加载自身管辖的类,跳过“先委托父加载器”的步骤。

(2)常见场景及原理

场景1:Tomcat的多Web应用类隔离
  • 核心需求:多个Web应用部署在同一个Tomcat中,需实现类隔离(如不同应用使用不同版本的Spring核心类,不冲突),同时支持热部署。

  • 实现方式:Tomcat为每个Web应用创建独立的WebAppClassLoader实例,每个实例都是独立的类加载器,打破双亲委派:

  1. 每个WebAppClassLoader优先加载本应用WEB-INF/classes和WEB-INF/lib下的类,不先委托父加载器;

  2. 不同Web应用的WebAppClassLoader是独立实例,即使加载了全限定名相同的类,由于“全限定名+类加载器”组合不同,JVM视为不同类,实现类隔离和版本共存;

  3. 热部署实现:当Web应用更新时,销毁旧的WebAppClassLoader实例,创建新的实例加载更新后的应用类,旧实例及加载的类会被GC回收,实现热替换,不影响其他Web应用运行。

场景2:Spring Boot自定义类加载器加载外部插件(插件化开发)
  • 核心需求:Spring Boot项目支持插件化开发,用户可上传外部插件jar包扩展功能(如低代码平台的报表插件、审批插件),需实现插件与核心类隔离、插件热部署、多版本共存。

  • 为什么打破双亲委派?

  1. 外部插件jar包不在父加载器的搜索路径中,若遵循双亲委派,向上委托一圈后仍找不到,最终还是要由自定义类加载器加载,会浪费无效的委托步骤;

  2. 避免版本冲突:应用核心类可能依赖某个类的特定版本(如1.0版本),而外部插件可能依赖该类的高版本(如2.0版本),通过自定义类加载器加载插件,可实现不同版本类的共存。

  • 核心实现逻辑:
  1. 自定义类加载器重写loadClass()方法,优先加载指定路径(如插件目录)下的外部jar包,不先委托父加载器;

  2. 加载时会检查当前自定义类加载器的缓存,判断插件类是否已加载,避免同一插件类被当前加载器重复加载;

  3. 热部署实现:通过文件监听机制(监听插件jar包的修改时间、文件大小变化)感知插件更新,当检测到更新时,销毁旧的自定义类加载器,创建新的自定义类加载器加载更新后的插件jar包,新加载器加载的类被JVM视为新类,完成热部署,不影响应用主系统运行。

  • 企业级实际案例:企业级低代码平台

低代码平台的核心需求是支持用户自定义扩展功能,用户可通过平台上传自定义插件jar包(如报表生成插件、审批流程插件、自定义表单插件等);这些插件由Spring Boot自定义类加载器优先加载,与平台核心类(如用户权限、基础配置类)隔离,即使插件类与核心类全限定名相同,也不会出现冲突;当用户需要更新插件(如修改报表模板、调整审批流程规则)时,无需重启低代码平台,通过热部署机制,销毁旧加载器、创建新加载器加载新版本插件,实现插件的无缝更新,保障平台核心功能的稳定运行。

2.5 补充:Spring Boot Starter与插件化开发的区别

两者均为Spring Boot项目的扩展方式,但核心机制完全不同,本质区别是“类加载方式”和“依赖管理方式”,实际开发中常配合使用:

(1)核心区别

  1. Spring Boot Starter:
  • 加载方式:通过Maven/Gradle依赖的方式引入,由应用默认类加载器加载,完全遵循双亲委派机制;

  • 本质:“编译时依赖合并”,Starter中的类与应用核心类在同一个类加载器下,共享应用的所有依赖和资源。

  1. 插件化开发(外部jar包):
  • 加载方式:由Spring Boot自定义类加载器加载,打破双亲委派机制;

  • 本质:“运行时类隔离加载”,插件类与应用核心类隔离(不同类加载器实例),可独立管理依赖和版本。

(2)优劣对比(无绝对优劣,按需选择)

  1. Spring Boot Starter:
  • 优势:开发简单、集成成本低,只需引入依赖即可使用;与应用深度集成,共享资源,适合基础功能的复用;遵循双亲委派,稳定性高,不易出现类冲突。

  • 劣势:无法实现类隔离和多版本共存;若Starter依赖的类版本与应用核心类版本冲突,需手动解决依赖冲突;无法实现热部署,更新Starter需重启应用。

  1. 插件化开发:
  • 优势:实现类隔离和多版本共存,避免插件与核心类的版本冲突;支持热部署,插件更新无需重启应用,提升开发和运维效率;功能生命周期独立,可按需加载、卸载插件,灵活性高。

  • 劣势:开发复杂度高,需自定义类加载器、实现文件监听和热部署逻辑;插件与应用核心类隔离,无法直接共享资源,需通过接口等方式实现交互。

(3)实际配合场景

  1. 核心基础功能:采用Spring Boot Starter引入,如数据库连接池(spring-boot-starter-jdbc)、缓存框架(spring-boot-starter-data-redis)、日志框架(spring-boot-starter-logback)等,这些功能长期稳定,无需频繁更新,适合深度集成。

  2. 业务扩展功能:采用插件化开发,如低代码平台的自定义插件、电商平台的营销活动插件(不同节日更新活动规则)、ERP系统的客户定制化插件(不同客户的个性化需求)等,这些功能生命周期独立、需频繁迭代,适合隔离加载和热部署。

示例:一个ERP系统中,基础的用户认证、权限管理功能通过Starter引入,保证核心稳定性;而不同客户定制的报表统计、流程审批功能,通过插件化加载,支持客户按需更新,无需重启整个ERP系统。

三、JVM内存模型(多线程安全的核心)

JVM内存模型(Java Memory Model,JMM)是JVM规范中关于多线程共享变量访问的规则,核心目的是“解决多线程下共享资源的同步性问题,保证数据的可见性、原子性和有序性”,避免出现脏读、重排序、线程安全问题,支撑多线程程序的正确运行。

注意:JVM内存模型与JVM内存结构是两个相关但不同的概念——内存结构是物理层面的内存划分(堆、栈、方法区等),解决的是内存管理和隔离问题;内存模型是逻辑层面的规则,解决的是多线程共享变量的访问安全问题,两者并行,共同构成JVM内存管理体系。

3.1 内存模型的核心抽象关系

JMM定义了线程和主内存之间的抽象关系,核心划分两个区域:

  1. 主内存:所有线程共享的内存区域,存放所有共享变量(实例变量、静态变量),对应JVM内存结构中的堆和方法区。

  2. 工作内存:每个线程私有,存放主内存中共享变量的副本,对应JVM内存结构中的虚拟机栈,线程操作共享变量时,只能操作自己工作内存中的副本,不能直接操作主内存中的变量。

线程操作共享变量的流程:

  1. 线程从主内存中读取共享变量,加载到自己的工作内存中;

  2. 线程在工作内存中对变量进行操作(修改、读取);

  3. 操作完成后,线程将工作内存中的变量副本写回主内存;

  4. 其他线程若需要访问该变量,需从主内存中重新读取,加载到自己的工作内存中。

3.2 内存模型要解决的三大问题(可见性、原子性、有序性)

由于线程操作共享变量时需经过“主内存→工作内存”的交互,可能出现三大问题,JMM通过定义规则和关键字(volatile、synchronized)来解决:

(1)可见性

  • 问题描述:一个线程修改了共享变量的值,写回主内存后,其他线程无法及时感知到该变化,仍使用自己工作内存中的旧副本,导致数据不一致。

  • 解决方式:通过volatile关键字实现可见性,volatile通过“内存屏障”机制,强制线程修改共享变量后,立即写回主内存;同时强制其他线程读取共享变量时,从主内存重新加载,避免使用旧副本。

(2)原子性

  • 问题描述:一个操作(如i++)看似是一个步骤,实际是“读取i的值→修改i的值→写回i的值”三个步骤,多线程并发执行时,可能出现步骤交错,导致结果错误(如两个线程同时执行i++,最终结果可能只加1,而非2)。

  • 解决方式:

  1. synchronized关键字:通过加锁机制,保证同一时刻只有一个线程能执行该操作,实现原子性;同时synchronized还能保证可见性和有序性。

  2. java.util.concurrent.atomic包下的原子类(如AtomicInteger):通过CAS(Compare and Swap)机制,实现无锁原子操作。

(3)有序性

  • 问题描述:JVM为了提升执行效率,会对代码进行“指令重排”(在不影响单线程执行结果的前提下,调整指令的执行顺序),但多线程环境下,指令重排可能导致线程执行结果不一致。

  • 解决方式:

  1. volatile关键字:通过内存屏障禁止指令重排,保证指令按顺序执行。

  2. synchronized关键字:保证同一时刻只有一个线程执行代码,避免指令重排带来的问题。

3.3 核心总结

JVM内存模型的核心是“定义多线程共享变量的访问规则”,通过volatile、synchronized等关键字,解决可见性、原子性、有序性问题,确保多线程程序的正确性。它与JVM内存结构并行,共同支撑Java程序的稳定运行——内存结构解决“内存如何管理”,内存模型解决“多线程如何安全访问内存”。

四、JVM垃圾回收机制(内存高效利用的核心)

垃圾回收(Garbage Collection,GC)是JVM内存管理的核心手段,核心目的是“自动识别并回收不再使用的对象,释放内存空间,避免内存泄漏和OOM,提高内存利用率”。垃圾回收的核心流程是“识别垃圾→回收垃圾→整理内存”,不同内存区域(新生代、老年代)适配不同的垃圾回收算法,同时有多种垃圾回收器可供选择,适配不同的应用场景。

4.1 垃圾回收的核心流程

垃圾回收的全过程分为三步,环环相扣,确保高效回收垃圾、合理利用内存:

(1)第一步:识别垃圾(判断哪些对象是“垃圾”)

垃圾的定义:不再被任何线程引用、无法被访问的对象,这些对象占用的内存可以被回收。

主流识别算法:可达性分析算法(目前所有主流JVM都采用此算法)

  • 核心原理:从“GC Roots”(垃圾回收根节点)开始,遍历对象的引用链,若一个对象没有任何引用链连接到GC Roots(即不可达),则标记为垃圾对象。

  • GC Roots的常见类型:虚拟机栈中引用的对象、方法区中静态变量引用的对象、方法区中常量引用的对象、本地方法栈中Native方法引用的对象。

  • 补充:早期的“引用计数法”已被淘汰,因为它无法解决“循环引用”问题(如两个对象互相引用,但都不再被其他对象引用,引用计数永远不为0,导致无法回收,造成内存泄漏);可达性分析算法能完美解决循环引用问题,准确识别垃圾。

(2)第二步:回收垃圾(选择算法回收垃圾)

回收垃圾的核心是“选择合适的算法”,不同算法的效率、适用场景不同,JVM会根据内存区域(新生代、老年代)的特点,选择对应的算法。目前主流的垃圾回收算法有3种:标记-清除、标记-复制、标记-整理。

算法1:标记-清除算法(Mark-Sweep)
  • 核心流程:分为“标记”和“清除”两个阶段:
  1. 标记阶段:遍历所有对象,标记出垃圾对象;

  2. 清除阶段:遍历所有对象,找到标记的垃圾对象,释放其占用的内存。

  • 优点:实现简单,不需要移动存活对象,回收时的“移动开销”小(适合老年代高存活率的对象)。

  • 缺点:

  1. 效率低:标记和清除两个阶段都需要全量遍历所有对象,当对象数量较多时,耗时较长;

  2. 产生内存碎片:清除垃圾后,会留下大量不连续的内存碎片,后续分配大对象时,可能找不到足够的连续空间,不得不提前触发GC,影响程序运行效率。

  • 适用场景:早期CMS收集器的老年代回收(目前最新JDK版本中已很少单独使用)。
算法2:标记-复制算法(Mark-Copy)
  • 核心流程:将内存划分为两个大小相等的区域(或多个区域),每次只使用其中一个区域;回收时,标记出该区域中的存活对象,将存活对象复制到另一个空闲区域,然后直接清空原区域的所有对象。

  • 优点:

  1. 效率高:无需全量遍历所有对象,只需复制存活对象,且存活对象少(如新生代)时,复制开销小;

  2. 无内存碎片:回收后原区域被清空,新区域的存活对象连续排列,后续分配大对象时无需担心碎片问题。

  • 缺点:需要划分出一部分内存作为“备用区”,平时不能用于对象分配,相当于浪费了一部分内存空间(空间换时间)。

  • 适用场景:新生代回收(新生代对象生命周期短、存活率低,通常低于10%,复制少量存活对象的开销小,空间浪费可接受)。

  • 优化:新生代的Survivor区采用“两个区交替使用”的设计(Eden:From Survivor:To Survivor = 8:1:1),实际可用空间为90%,平衡了效率和空间利用率。

算法3:标记-整理算法(Mark-Compact)
  • 核心流程:分为“标记”“整理”“清除”三个阶段:
  1. 标记阶段:遍历所有对象,标记出垃圾对象;

  2. 整理阶段:将所有存活对象向内存一端移动,修改对象的引用地址,确保存活对象连续排列;

  3. 清除阶段:清空内存另一端的垃圾对象,释放内存。

  • 优点:无内存碎片,能充分利用内存空间;适合高存活率的对象,避免碎片导致的大对象分配失败。

  • 缺点:效率中等,比标记-复制算法多了“移动存活对象”的步骤,移动过程需要修改大量对象的引用地址,开销较大。

  • 适用场景:老年代回收(老年代对象生命周期长、存活率高,移动开销虽大,但能避免碎片问题,保证内存长期稳定使用)。

(3)第三步:整理内存(优化内存空间)

整理内存的核心目的是“减少内存碎片,提高内存利用率”,不同算法的整理方式不同:

  1. 标记-清除算法:无整理步骤,直接清除垃圾,产生碎片;

  2. 标记-复制算法:通过复制存活对象到新区域,天然实现整理,无碎片;

  3. 标记-整理算法:通过移动存活对象,主动整理内存,无碎片。

4.2 分代回收策略(JVM默认的回收策略)

分代回收是JVM结合不同内存区域的特点,采用“不同区域用不同算法”的组合策略,核心是“按对象生命周期分代,针对性回收”,兼顾效率和空间利用率:

  1. 新生代:对象生命周期短、存活率低,采用“标记-复制算法”,效率高、无碎片;

  2. 老年代:对象生命周期长、存活率高,采用“标记-整理算法”(或早期的标记-清除算法),无碎片、充分利用内存;

  3. Survivor区的作用:作为新生代和老年代之间的“缓冲器”,筛选长生命周期对象——短期对象在Eden和Survivor之间经过多次GC后被回收,只有多次存活的对象才进入老年代,减少老年代的GC频率,避免老年代频繁回收的性能损耗。

补充:若新生代没有Survivor区,直接将Eden区存活对象复制到老年代,会导致老年代很快被短期对象占满,老年代GC(Full GC)频率增加,而老年代GC效率低,会严重影响程序性能。

4.3 主流垃圾回收器(按递进关系梳理)

垃圾回收器是垃圾回收算法的具体实现,不同回收器的核心目标、适用场景不同,发展历程是“从追求吞吐量→追求低延迟→追求超大堆+极致低延迟”,按递进关系梳理如下(核心突出“解决的问题+核心特点+缺陷”):

(1)Parallel Scavenge(并行回收器)

  • 核心目标:解决“单线程GC效率低”的问题,提升吞吐量(吞吐量=程序运行时间/(程序运行时间+GC时间))。

  • 核心特点:多GC线程并行回收,GC线程之间并行执行,加快GC速度,减少单次GC的时间;新生代用标记-复制算法,老年代用标记-整理算法。

  • 缺陷:GC线程并行执行时,应用线程会全程暂停(Stop-The-World,STW),停顿时间不可控,延迟较高,不适合对延迟敏感的场景(如Web应用)。

  • 适用场景:吞吐量优先的场景,如后台计算、大数据处理等。

(2)CMS(Concurrent Mark Sweep,并发标记清除回收器)

  • 核心目标:解决Parallel Scavenge“停顿时间长”的问题,降低延迟,减少应用线程的停顿时间。

  • 核心特点:老年代采用“标记-清除算法”,将GC过程拆分为4个阶段,其中“并发标记”和“并发清除”阶段,GC线程与应用线程并发执行,仅“初始标记”和“重新标记”阶段需要暂停应用线程(这两个阶段耗时极短),大幅缩短STW时间。

  • 缺陷:

  1. 内存碎片:老年代用标记-清除算法,会产生内存碎片,可能导致大对象分配失败,触发Full GC;

  2. CPU占用高:并发阶段,GC线程会占用部分CPU资源,可能影响应用线程的响应速度;

  3. 无法处理浮动垃圾:并发清除阶段,应用线程可能产生新的垃圾,这些垃圾无法在本次GC中回收,需等到下次GC。

  • 补充:CMS可通过配置-XX:CMSFullGCsBeforeCompaction参数,指定执行N次标记-清除Full GC后,触发一次标记-整理的Full GC,缓解内存碎片问题(但整理阶段会导致较长停顿)。

  • 适用场景:低延迟优先的场景,如Web应用、接口服务等。

(3)G1(Garbage-First,垃圾优先回收器)

  • 核心目标:解决CMS“内存碎片+停顿不可控”的问题,平衡吞吐量和延迟,实现“可控停顿时间”。

  • 核心特点:

  1. 区域化回收:将堆内存划分为多个大小相等的Region(区域),每个Region可动态作为新生代或老年代,打破传统的固定分代模式;

  2. 垃圾优先:根据每个Region的垃圾占比(回收收益),优先回收垃圾最多、回收收益最高的Region;

  3. 可控停顿:结合用户设置的停顿时间目标,调整本次回收的Region数量,确保GC停顿时间不超过目标值;

  4. 算法组合:新生代和老年代均采用标记-复制算法,天然减少内存碎片,整体结合标记-整理思想。

  • 缺陷:大堆(如TB级)场景下,延迟仍有优化空间,GC停顿时间会略有增加。

  • 适用场景:大堆场景(4GB以上),兼顾吞吐量和延迟,如中大型Web应用、分布式系统等。

(4)ZGC(Z Garbage Collector,低延迟回收器)

  • 核心目标:解决G1“大堆延迟高”的问题,实现“极致低延迟+超大堆支持”,让GC停顿时间独立于堆大小。

  • 核心特点:

  1. 并发移动对象:通过“着色指针”(逻辑概念,在指针中预留几位作为标记位,存储对象的状态和新地址)和“读屏障”,实现对象的并发移动,无需暂停应用线程;

  2. 延迟极低:单次GC停顿时间控制在毫秒级以下,且不随堆大小增长而增加(即使堆内存达到TB级,停顿时间仍保持稳定);

  3. 无内存碎片:采用标记-复制算法,回收时将存活对象复制到新Region,避免碎片。

  • 核心原理:对象移动时,不立即遍历所有引用修改地址,而是让应用线程访问对象时,通过读屏障检查着色指针,若发现对象已移动,根据标记位中的新地址进行转发,实现“延迟修正引用”,避免全局停顿。

  • 适用场景:超大堆(TB级)、低延迟优先的场景,如高并发交易系统、大数据处理系统等。

(5)补充:其他回收器

  1. Shenandoah GC:Red Hat主导开发,与ZGC类似,核心目标是低延迟、超大堆支持,通过“连接矩阵”和“转发指针”实现并发移动对象,与ZGC的技术路径不同,在部分硬件或JDK版本下有更灵活的CPU利用优化。

  2. Epsilon GC:“无操作”回收器,完全不回收内存,仅用于特殊测试场景(如测试对象分配速度、确定应用内存需求上限)。

4.4 垃圾回收器的发展逻辑

垃圾回收器的迭代,本质是“应对应用场景的需求升级”:

  1. 早期:应用对吞吐量要求高,Parallel Scavenge通过多线程并行回收,提升吞吐量,但延迟高;

  2. 中期:Web应用兴起,对延迟要求提高,CMS通过并发回收,缩短停顿时间,但存在碎片和CPU占用问题;

  3. 后期:大堆应用增多,需要平衡吞吐量和延迟,G1通过区域化回收和可控停顿,满足中大型堆需求;

  4. 最新:超大堆、极致低延迟需求(如高并发交易),ZGC、Shenandoah通过并发移动对象和着色指针,实现TB级堆和毫秒级停顿。

五、JVM调优核心思路(实际开发必备)

JVM调优的核心是“定位问题→针对性优化”,并非上来就切换垃圾回收器,而是先通过监控工具定位问题,再逐步优化,核心围绕“CPU、内存、IO”三大指标(三者相互影响):

5.1 调优前提:定位问题

  1. 监控指标:重点关注GC日志、堆内存使用情况(新生代、老年代占用率)、GC频率、STW时间、CPU使用率、IO使用率;

  2. 常见问题:

  • 内存问题:OOM、频繁Full GC、内存碎片过多;

  • CPU问题:GC线程频繁运行、并发GC占用过多CPU;

  • IO问题:GC日志输出频繁、内存交换(Swap)频繁。

5.2 核心调优步骤

  1. 优先优化内存参数:调整新生代、老年代大小,如增大新生代减少Minor GC次数,调整Survivor区比例,避免大对象直接进入老年代;

  2. 优化对象生命周期:减少临时对象的创建,避免内存泄漏(如关闭资源、释放无用引用);

  3. 选择合适的垃圾回收器:根据应用场景选择(吞吐量优先选Parallel Scavenge,低延迟选CMS/G1,超大堆选ZGC);

  4. 调整GC参数:如设置GC停顿时间目标、调整元空间大小、配置CMS的碎片整理参数等;

  5. 验证优化效果:监控优化后的GC频率、STW时间、CPU/IO使用率,逐步调整参数,直至达到预期效果。

5.3 补充:内存碎片与CPU、IO的关联

内存碎片过多会间接导致CPU和IO升高:

  1. 内存碎片多→大对象分配失败→频繁触发Full GC→GC线程占用大量CPU;

  2. 频繁Full GC→内存不足→操作系统触发内存交换(将内存数据写入磁盘)→产生大量IO操作。

六、核心总结(四大模块逻辑关系)

JVM四大核心模块层层递进,共同支撑Java程序的稳定、高效运行,可通过通俗类比理解:

  1. JVM内存结构:相当于“工厂的不同车间”,按功能划分(堆=长期仓库、栈=临时工作台、方法区=原料库),是内存管理的物理基础;

  2. 类加载机制:相当于“原料检验与入库流程”,将.class文件(原料)安全、准确地加载到方法区(原料库),为程序执行提供原料;

  3. JVM内存模型:相当于“多工人协作规则”,当多个线程(工人)同时操作堆/方法区(仓库)中的共享资源(物料)时,通过规则保证操作不冲突、数据不出错;

  4. 垃圾回收机制:相当于“仓库清理工人”,自动识别并清理仓库中无用的物料(垃圾对象),释放空间,避免仓库堆满(OOM),提高仓库利用率。

作为架构师/高级工程师,掌握JVM四大核心模块,能深入理解Java程序的运行原理,快速定位和解决JVM相关的性能问题、内存问题,是设计高可用、可扩展、低延迟系统的重要基础。

揭秘JVM:Java虚拟机的核心奥秘 JVM(Java虚拟机)是Java程序运行的核心环境,负责字节码执行和内存管理。其核心机制包括类加载、运行时数据区(堆、栈、方法区等)、执行引擎(解释器/JIT)和垃圾回收。优化JVM需关注内存参数(如-Xms/-Xmx设置堆大小)、GC策略选择(G1/ZGC等)、线程栈配置及容器适配。通过工具(jmap/jstack/Arthas)可诊断内存泄漏、线程阻塞等问题,而JIT编译优化和CDS技术能提升性能。合理的JVM调优需结合应用特征,平衡吞吐量与延迟。 阅读详情

相关推荐

《offer 来了:Java 面试核心知识点精讲 -- 原理篇》

在Java面试的战场上,只知皮毛可不行,面试官们越来越看重对原理的理解。今天就给大家分享一本能让你在面试中脱颖而出的“武林秘籍”——《offer来了:Java面试核心知识点精讲--原理篇》。深入讲解Java虚拟机;并发编程攻克面试难点.

dreamer23的专栏 1223

Java虚拟机(JVM)核心知识点解析

Java虚拟机(JVM)是Java程序运行的核心环境,理解JVM的工作原理对于Java开发者来说至关重要。本文将从JVM的基本概念、内存区域、对象管理、垃圾回收机制、类加载机制、程序编译与优化等方面进行详细解析,帮助读者深入理解JVM的内部机制。

专注Java和MySQL领域知识分享 1415

Java面试必备知识点大全

本文还有配套的精品资源,点击获取 简介:Java面试题是程序员求职中的重要环节,旨在评估应聘者在基础知识、编程能力、并发处理等多方面技能。本文列举了Java面试中常见的核心知识点,包括Java基础、数据类型与集合框架、多线程编程、IO流与NIO、反射机制、设计模式、JVM原理、并发工具类、Spring框架以及数据库连接等方面。求职者应深入理解每个知识点,并结合实战经验,以展...

weixin_42599908的博客 1927

JVM——Java虚拟机架构

0. 前言 Java虚拟机(Java virtualmachine)实现了Java语言最重要的特征:即平台无关性。 平台无关性原理:编译后的Java程序(.class文件)由JVM执行。JVM屏蔽了与具体平台相关的信息,使程序可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。因此实现Java平台无关性。 1. JVM结构图 ...

关于Android开发的一些技术点总结 ╮( ̄▽ ̄”)╭ 3万+

高效利用ChEMBL数据库:从靶点查询到活性数据处理的完整指南

本文提供了高效利用ChEMBL数据库的完整指南,从靶点查询到活性数据处理。详细介绍了靶点精准定位、高级过滤技巧、数据导出优化以及活性数据清洗、特征工程和可视化等实战技巧,帮助药物研发人员快速获取和处理生物活性数据,提升工作效率。

weixin_29278805的博客 798

XC7K325T FPGA硬件设计实战:从板级原理图到高速接口优化

本文以XC7K325T FPGA为核心,详细阐述了从板级原理图设计到高速接口优化的完整硬件实战流程。内容涵盖芯片管脚与电源规划、时钟复位配置电路设计,并重点解析了DDR3内存子系统以及PCIe与光纤接口等高速电路的原理图与PCB设计要点,旨在帮助工程师构建稳定可靠的高性能硬件平台。

python9snake的博客 513

【Java虚拟机JVM】面试知识点总结

JVM是Java虚拟机,存在于JRE中,即Java运行时环境,JVM时运行在操作系统上的程序,而我们的Java程序是运行在JRE(JVM)之上的,操作系统是运行在硬件之上的。 我们所编写的Java文件,首先会被编译成class文件,然后通过类加载器进行加载到JVM中,也就是我们的运行时数据区,核心有五部分组成,分别是,方法区、堆、虚拟机栈、本地方法栈、程序计数器。 ...

3389

Neo4j Desktop新手必看:3分钟搞定CSV数据导入(附常见报错解决方案)

本文详细介绍了在Neo4j Desktop中高效导入CSV数据的三种主流方法,包括图形化界面操作、命令行批量导入和Python驱动编程导入,并提供了常见报错解决方案和性能优化技巧,帮助新手快速构建知识图谱。

lambda的博客 648

【jvm系列-01】初识虚拟机与java虚拟机

为了解决java的跨平台性问题,

zhenghuishengq的博客 9756

设计的多实例引用(Multiple Instances of a Design Reference)

一、什么是“设计的多实例引用” 在层次化的设计中,我们经常会将一个module例化多份,这些例化的instance就是设计的module的多实例引用。 二、为什么要解决多实例引用 三、怎么解决多实例引用 ...

kevindas的博客 3719

【Python】秋招机考 —— 2025/9/17 华为 AI 方向

秋招机考,华为 AI,使用 Python 完成编程。

一杯水果茶!足矣~ 1166

Java虚拟机5大核心知识点

在这个部分总结了2019年到目前为止Java常见面试问题,取其面试核心编写成这份文档笔记,从中分析面试官的心理,摸清面试官的“套路”,可以说搞定90%以上的Java中高级面试没一点难度。本节总结的内容涵盖了:消息队列、Redis缓存、分库分表、读写分离、设计高并发系统、分布式系统、高可用系统、SpringCloud微服务架构等一系列互联网主流高级技术的知识点。(上述只是一个整体目录大纲,每个点里面都有如下所示的详细内容,从面试问题——分析面试官心理——剖析面试题——完美解答的一个过程)

2401_84010402的博客 1036

SXU-软件项目管理-朱少民-第九章 项目监控与控制 第十章 项目收尾 附相关习题

软件项目管理-朱少民-第九章 项目监控与控制 附相关习题

weixin_63597914的博客 1488

【Java 基础(人话版)】Java 虚拟机(JVM)

它负责把 Java 代码转换成计算机能识别的机器语言,并且让不同操作系统上的 Java 程序表现一致。JVM 负责运行 Java 代码,使得 Java 具有良好的跨平台能力。,都能保证相同的 Java 代码在不同平台上运行时,结果是一致的。不同 JVM 各有特点,适用于不同的场景。只是 JVM 规范的一种实现方式,除此之外,还有。,任何公司、组织甚至个人都可以实现自己的 JVM。JVM 运行时会划分出不同的内存区域,用来存储。等不同系统,所有 JVM 只要遵循相同的。不同 JVM 适用于不同场景,例如。

qq_45729306的博客 1万+

Linux安装MinIO(图文解说详细版)

最近开个新坑,就是在linux环境中操作开发环境,带大家玩转Linux,会整理出一篇Linux的专栏,欢迎大家订阅!! 富贵同学linux环境为CentOS7.8 版本。 上期跟大家说了一下OpenJDK的安装,这次说一下minio的安装 第一步,进入/opt 目录,创建minio文件夹 cd /opt mkdir minio 第二步,wget下载安装包: wget https://dl.minio.io/server/minio/release/linux-amd64/minio 第三步,进入m

编码人生 5万+

ABAP CDS View 从入门到精通教程第一篇:ABAP CDS View 在 SAP 技术体系中的重要地位

摘要:本文介绍了SAP开发技术专家提供的多套SAP开发教程,涵盖ABAP、UI5、Fiori、OData、BTP等技术领域。重点阐述了ABAP CDS View在SAP S/4HANA中的核心地位,作为虚拟数据模型(VDM)的关键技术,它实现了OLTP和OLAP的统一处理。文章还说明了学习CDS View的前置条件,包括需要SAP NetWeaver 7.50 SP01或更高版本系统,以及使用SAP ABAP Development Tool进行开发实践。作者具有16年SAP开发经验,这些教程形成了完整的S

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 899

JVM_01_JVM核心知识点图解

文章目录前言推荐阅读一、JVM的运行时数据区概览1.程序计数器2.虚拟机栈2.1 虚拟机栈2.2 局部变量表2.3 操作数栈2.4 动态连接2.5 方法返回地址3.堆4.方法区二、HotSpot虚拟机对象探秘1.对象的内存结构2.对象的访问定位3.对象的创建三、类加载机制1.类的生命周期2.类加载的时机3.类加载过程3.1 加载3.2 验证3.3 准备3.4 解析3.5 初始化4.类加载器的双亲委...

shirayner 1539

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

JVM_Java虚拟机<深入理解Java虚拟机>走进JVM

由于跨平台性的设计,Java的指令都是根据栈来设计的。不同平台CPU架构不同,所以不能设计为基于寄存器的。优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样的功能需要更多的指令。时至今日,尽管嵌入式平台已经不是Java程序的主流运行平台了(准确来说应该是HotSpotVM的宿主环境已经不局限于嵌入式平台了),那么为什么不将架构更换为基于寄存器的架构呢?栈:跨平台性、指令集小、指令多、执行性能比寄存器差言包括:Java、Scala、Groovy、Kotlin;

Coastlise的博客 1168

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

JVM 详解:Java 虚拟机的核心机制

JVM(Java Virtual Machine,Java 虚拟机)是 Java 程序运行的核心环境。它是一个抽象的计算机,负责将 Java 字节码(.class 文件)解释或编译为特定平台的机器指令,从而实现 Java 的“一次编写,到处运行”的特性。JVM 是 Java 生态的基石,理解其核心机制对于开发高性能、高可靠性的 Java 应用至关重要。通过深入掌握 JVM 的架构、工作原理和调优技巧,开发者可以更好地应对复杂的应用场景,提升系统的稳定性和效率。

weixin_58316280的博客 2224

无虚拟机不Java,JVM必备核心知识,从入门到大厂

无虚拟机不Java,JVM必备核心知识,从入门到大厂 前言: JVM 的内存模型和 JVM 的垃圾回收机制一直是 Java 业内从业者绕不开的话题(实际调优、面试)JVM是java中很重要的一块知识,也是面试常问的问题之一. 虚拟机概念:指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的计算机系统 JVM 内存划分与内存溢出异常 概述 如果在大学里...

Javazhoumou的博客 407

Java JVM虚拟机学习的经典书籍

1、深入理解Java虚拟机——JVM高级特性与最佳实践(第2版) 基于最新JDK1.7,围绕内存管理、执行子系统、程序编译与优化、高效并发等核心主题对JVM进行全面而深入的分析,深刻揭示JVM的工作原理。以实践为导向,通过大量与实际生产环境相结合的案例展示了解决各种常见JVM问题的技巧和实践 2、揭秘Java虚拟机:JVM设计原理与实现 《揭秘Java虚拟机:JVM设计原理与实现》从源码角度解...

猎人在吃肉 7905

JVM(Java虚拟机)

JVM 作为 Java 的核心组成部分,提供了丰富的功能和机制,在本篇文章中,我们主要学习 JVM 的内存区域划分、类加载机制 和 垃圾回收算法

2301_76161469的博客 1517

理解JVM并不难,看完阿里大佬整理的JVM文档,看完手撕面试官

理解JVM并不难,看完阿里大佬整理的JVM文档,看完手撕面试官 提到Java虚拟机(JVM),可能大部分人的第一印象是“难”,但当让我们真正走入“JVM世界”的时候,会发现其实问题并不像我们想象中的那么复杂。唯一真正令我们恐惧的,其实是恐惧本身。 JVM 的内存模型和 JVM 的垃圾回收机制一直是 Java 业内从业者绕不开的话题(实际调优、面试)JVM是java中很重要的...

Javazhoumou的博客 5806

2019年面试必备(Java最新核心知识点整理)——JVM

前言: 听说你在面试 又到了求职的金三银四的黄金月份,我相信有不少小伙伴已经摩拳擦掌的准备寻找下一份工作。 就目前国内的面试模式来讲,在面试前积极的准备面试,复习整个 Java 知识体系将变得非常重要,可以很负责任的说一句,复习准备的是否充分,将直接影响你入职的成功率。 但很多小伙伴却苦于没有合适的资料来回顾整个 Java 知识体系,或者有的小伙伴可能都不知道该从哪里开始复习。 我偶然从...

weixin_45127309的博客 1336

JVM核心知识点解析

Java虚拟机(JVM)是一个执行Java字节码的虚拟机,它提供了一个抽象的计算机环境,具有独立的指令集和内存模型,能够在多种操作系统和硬件平台上运行。JVM的主要职责是执行Java程序,它负责加载、验证、执行Java字节码,并管理内存和垃圾回收。通过这种方式,JVM确保了Java程序的跨平台性和安全性。

JavaCodePro的博客 584

JVM核心知识点:深入解析与优化

在深入探讨Java虚拟机(JVM)的各个核心知识点之前,我们首先需要对其有一个全面而清晰的认识。想象一下,一个复杂的软件系统,如大型企业级应用,其背后运行的是由数以万计的Java程序组成的庞大程序集。这些程序如何高效、稳定地运行?这就离不开JVM这个核心组件。JVM,即Java虚拟机,是Java程序运行的基础环境。它负责将Java字节码转换为机器码,从而在底层操作系统上执行。在当前的技术环境中,JVM扮演着至关重要的角色。

AIHacksCash的博客 971
上一篇: Java类加载机制(架构师_高级工程师必备总结)
下一篇: Java虚拟机(JVM)核心知识梳理与深层解析
Coder_Boy_
博客等级 码龄10年 2804粉丝 819原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coder_Boy_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值