JVM虚拟机调优实战
2026/6/10 3:56:57 网站建设 项目流程

参考:

2025年讲的最好的JVM虚拟机实战教程(JVM内存模型+JVM调优+JVM底层原理+JVM面试题)比啃书效果好多了,一周学完让你少走99%的弯路!_哔哩哔哩_bilibili

一、JDK的体系结构

1.为什么Java可以跨平台

1. 编译阶段:一次编译生成统一字节码

.java源文件通过javac编译命令,编译生成 ".class字节码文件"字节码是一套不绑定 Windows/Linux/macOS 任何操作系统、独立于硬件平台的中间指令,只面向 JVM 规范,和底层系统无关。

2. 运行阶段:不同系统安装对应版本 JVM

使用java命令运行.class时:

  • Windows 系统安装Windows 版 JVM
  • Linux 系统安装Linux 版 JVM

JVM 是平台相关的:不同操作系统有各自适配系统内核的 JVM 实现,由厂商针对系统单独开发。

3. JVM 屏蔽底层系统差异(核心)

JVM 本质是一个虚拟计算机,在软件层隔离操作系统、CPU 硬件的底层指令差异:同一份.class字节码,交给对应系统的 JVM 后,JVM 会把统一字节码翻译成当前本机操作系统能识别的机器码执行。

总结一句话

一次编译,到处运行:源文件统一编译成与平台无关的 class 字节码,依靠各操作系统专属的 JVM 做适配翻译,实现跨平台。

关键点:Java 程序 (.class) 跨平台,JVM 本身不跨平台

二、JVM虚拟机内存模型

1. 程序计数器【线程私有】

  • 作用:记录当前线程正在执行的字节码行号,用于线程切换后恢复执行位置、指引字节码引擎顺序执行指令;
  • 唯一没有 OOM(内存溢出)的内存区域
  • 执行 native 本地方法时,计数器值为空。

2. 栈(线程)【线程私有】

①、基础特性:线程私有

  1. 一个线程对应一个独立虚拟机栈,线程创建,栈同步开辟;线程销毁,栈立刻自动回收内存,不存在 GC 回收栈内存。
  2. 生命周期和线程完全绑定,多线程之间虚拟机栈互相隔离、互不干扰。

②、内部结构:栈帧(核心)

虚拟机栈由多个栈帧(Frame)组成,每调用 1 个方法,就往栈顶压入 1 个栈帧;方法执行完毕,栈帧出栈销毁,遵循后进先出栈结构。单个栈帧包含 4 部分:

  1. 局部变量表存放方法内的局部变量:基本数据类型、对象引用地址,编译期就确定内存大小。
  2. 操作数栈临时存放运算数据,做加减、赋值等运算时,数据入栈计算、结果出栈。
  3. 动态链接指向方法区常量池,将符号引用转为实际方法地址。
  4. 方法返回地址记录方法结束后,回到上层调用方法的执行位置。

③、两种异常情况

  1. StackOverflowError(栈溢出)单个线程不断递归调用方法,栈帧数量超出虚拟机栈固定容量,栈装满报错。
  2. OutOfMemoryError(OOM)虚拟机栈支持动态扩容,操作系统无法继续分配新内存给栈时,报内存溢出。

3. 本地方法栈【线程私有】

  • 和虚拟机栈结构功能近似,专门服务于 Native 本地方法(C/C++ 编写的底层方法)
  • 负责调用操作系统底层原生方法,同样会抛出栈溢出与 OOM 异常。

4. 堆(Heap)【线程共享,GC 重点区域】

  • JVM 中最大的一块内存,所有线程共享;
  • 唯一目的:存放所有 Java 对象实例、数组对象,绝大多数对象在这里创建;
  • 是垃圾回收(GC)最主要区域,分新生代、老年代;
  • 堆内存耗尽无法分配对象 →OutOfMemoryError堆内存溢出。

5. 方法区(JDK8 后→元空间 Metaspace)【线程共享】

JDK1.8 重大改动:方法区从堆中剥离,落地到操作系统本地内存,改名元空间

  • 存储内容:已加载类的类信息、常量、静态变量、运行时常量池、编译后的即时代码;
  • 元空间使用本地内存,默认无固定上限,内存耗尽同样触发 OOM;

补充:两个外部协作组件(流程图右侧)

  1. 类加载子系统:负责加载.class字节码,解析后将类元数据存入方法区、类对象放入堆;
  2. 字节码执行引擎:读取各内存区数据,解释 / 编译字节码,驱动程序运行。

三、堆内存细分:老年代与新生代

  1. 分代设计初衷:不同对象存活生命周期差异极大,分代搭配不同 GC 回收算法,大幅提升 GC 效率、降低 STW 停顿时间。
  2. 新生代(Young):存放新创建、短生命周期临时对象,占据堆约 1/3 空间;内部划分为 Eden 伊甸区、Survivor0、Survivor1 两个幸存区,使用复制清除算法回收。
  3. 老年代(Old):存放长期存活、大体积对象、多次 GC 后存活晋升的对象,占据堆约 2/3 空间;使用标记整理 / 标记清除算法回收。
  4. 对象晋升规则:新生代对象经历指定次数 Minor GC 后依旧存活,自动晋升到老年代;超大对象直接绕过新生代分配到老年代。

四、STW 全局暂停机制

  1. 定义:Stop-The-World,GC 执行期间暂停全部用户业务线程,只允许 GC 后台线程运行;所有主流垃圾回收器都存在 STW,仅停顿时长有差异。
  2. 产生原因:GC 标记、整理、复制对象时,如果业务线程同步创建 / 修改对象,会造成引用关系错乱、内存碎片紊乱,必须冻结所有业务线程保证 GC 准确性。
  3. 影响与优化方向:STW 停顿直接影响接口响应、高并发系统吞吐量;调优核心目标就是缩小 STW 时长(例如使用 ZGC、Shenandoah 低延迟回收器)。

五、JVM 调优工具 Arthas 详解

  1. 定位:阿里开源 Java 线上诊断神器,无需重启服务、无需修改业务代码,实时监控 JVM 运行状态。
  2. 核心常用能力:
    • 监控方法调用耗时、调用次数、异常率;
    • 查看类加载信息、JVM 内存实时占用、GC 次数与耗时;
    • 反编译线上 Class、动态修改日志级别、排查死锁线程;
    • 线上实时定位 OOM、CPU 飙高、线程阻塞等疑难故障。

六、实战:双十一亿级电商网站 JVM 参数调优

  1. 业务痛点:大促峰值百万 QPS,接口超时、频繁 Full GC、长时间 STW、偶发 OOM。
  2. 调优完整流程:1)压测采集基线数据:GC 频率、STW 耗时、堆内存占用、CPU 负载;2)选型低延迟垃圾回收器(ZGC/G1);3)精细划分新生代 / 老年代内存比例、调整对象晋升阈值;4)优化大对象分配、关闭冗余 JIT 编译参数;5)上线灰度放量,持续监控 GC 日志、接口 RT、吞吐量,迭代微调参数。

七、JVM 高频 10 道面试题详解

01 OOM 一定会导致 JVM 退出吗?

不一定。只有主线程抛出未捕获 OOM、或核心守护线程崩溃时 JVM 才会退出;普通子线程抛出 OOM 仅当前线程终止,剩余线程可正常运行,JVM 进程存活。

02 对象一定分配在堆中吗?

不是。JIT 逃逸分析开启后,方法内未逃逸的局部小对象会栈上分配,直接存放在虚拟机栈栈帧,方法结束随栈帧销毁,不经过 GC;超大对象、全局逃逸对象依旧分配堆。

03 内存泄漏和内存溢出的区别

  1. 内存泄漏:程序持有无效对象引用,GC 无法回收这块内存,内存占用缓慢持续上涨,属于过程问题;长期泄漏最终一定会诱发 OOM 溢出。
  2. 内存溢出 OOM:内存空间已经耗尽,无法分配新内存,程序抛出异常崩溃,属于最终结果

04 JVM 对象分配内存如何保证线程安全

两种方案:

  1. 内存分配同步锁:CAS + 自旋锁保证堆内存分配原子性;
  2. TLAB 线程本地分配缓冲区:每个线程预分配一小块私有堆内存,优先在 TLAB 创建对象,无竞争;TLAB 耗尽再走全局堆同步分配。

05 堆一定是线程共享的吗?

整体堆空间属于线程共享,但 TLAB(线程本地分配缓冲区)是堆划分给单线程独占的小块内存,线程之间 TLAB 隔离,仅共享堆主空间。

06 Class 常量池和运行时常量池的区别

  1. Class 常量池:存在于.class文件中,编译期生成,存储字面量、符号引用;
  2. 运行时常量池:类加载后,Class 常量池内容载入方法区的运行时常量池,支持运行期动态添加常量(String.intern())。

07 运行时常量池和字符串常量池的区别

  1. 运行时常量池:隶属于方法区,存储所有类型常量、符号引用;
  2. 字符串常量池:JDK8 存放在堆中,专门缓存 String 字符串对象,intern()方法控制字符串入池复用,减少重复字符串内存占用。

08 什么情况会导致 JVM 退出

  1. 正常退出:所有非守护业务线程全部执行完毕;调用System.exit(0)主动正常退出;
  2. 异常退出:主线程未捕获致命异常 / OOM;JVM 内部崩溃、操作系统强制 kill 进程;硬件资源耗尽。

09 JVM 内存为什么要分代

  1. 对象存活周期两极分化:大部分对象朝生夕灭,少量对象长期存活;
  2. 分代适配不同 GC 算法:新生代用高效复制算法,老年代用标记整理,平衡回收速度、内存碎片、CPU 开销;
  3. 大幅减少 Full GC 频率,Full GC 停顿远大于 Minor GC,分代能极大降低全局 STW 耗时。

10 GC 是任意时候都能进行的吗

不可以。GC 触发有严格时机限制:

  1. 新生代 Eden 区填满触发 Minor GC;老年代空间不足、空间担保失败触发 Full GC;
  2. 代码同步块、native 方法执行、JIT 编译关键阶段、栈帧完整执行过程中不会中断执行强制 GC;
  3. GC 执行全程触发 STW,必须等业务线程到达安全点(SafePoint)才可以启动回收。

八、Java 完整学习路线图(配套拓展)

  1. 基础阶段:Java 语法、面向对象、集合、IO、多线程、反射、异常;
  2. 进阶核心:JVM 完整体系(本文内容)、并发包、NIO、Lambda、JUC 锁;
  3. 框架工程:Spring/SpringBoot、MyBatis、SpringCloud 微服务;
  4. 中间件:Redis、MQ、Elasticsearch、MySQL 调优;
  5. 高阶实战:分布式事务、分库分表、亿级流量调优、容器 K8s、架构设计。

结尾总结

JVM 是 Java 后端面试核心重难点,吃透运行时内存分区、GC 分代、STW、调优工具与实战场景,能应对 80% 企业 JVM 面试提问;日常开发中掌握 Arthas 诊断、GC 日志分析、参数调优,可独立负责线上高并发服务稳定性保障。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询