目录
[1.1 大白话理解JVM](#1.1 大白话理解JVM)
[1.2 JVM架构](#1.2 JVM架构)
[1.3 跨平台运行的本质](#1.3 跨平台运行的本质)
[1.1 类加载全过程](#1.1 类加载全过程)
[1.1.1 加载阶段](#1.1.1 加载阶段)
[1.1.2 验证阶段](#1.1.2 验证阶段)
[1.1.3 准备阶段](#1.1.3 准备阶段)
[2.2 双亲委派机制](#2.2 双亲委派机制)
[2.3 自定义类加载器](#2.3 自定义类加载器)
[3.1 堆内存结构](#3.1 堆内存结构)
[3.1.1 新生代参数优化](#3.1.1 新生代参数优化)
[3.1.2 内存分配策略](#3.1.2 内存分配策略)
[3.2 虚拟机栈运行原理](#3.2 虚拟机栈运行原理)
[2.3 方法区演进](#2.3 方法区演进)
[4.1 解释器与JIT协作](#4.1 解释器与JIT协作)
[4.2 JIT优化技术](#4.2 JIT优化技术)
[4.2.1 方法内联优化](#4.2.1 方法内联优化)
[4.2.2 逃逸分析优化](#4.2.2 逃逸分析优化)
[4.3 AOT编译实践](#4.3 AOT编译实践)
[5.1 对象存活判定算法](#5.1 对象存活判定算法)
[5.1.1 可达性分析算法](#5.1.1 可达性分析算法)
[5.1.2 引用类型进阶](#5.1.2 引用类型进阶)
[5.2 经典GC算法对比](#5.2 经典GC算法对比)
[5.3 主流垃圾收集器](#5.3 主流垃圾收集器)
[5.3.1 Parallel收集器家族](#5.3.1 Parallel收集器家族)
[5.3.2 CMS收集器](#5.3.2 CMS收集器)
[5.3.3 G1收集器革新](#5.3.3 G1收集器革新)
一、JVM概述
1.1 大白话理解JVM
JVM是 Java Virtual Machine 的缩写,简单来说,JVM 就像是一台虚拟的小电脑,它能运行在 Windows、Linux 等各种各样的操作系统环境里。不过它可不直接和硬件打交道,而是和操作系统进行沟通交流,让操作系统帮忙去完成和硬件交互的那些事。

打个比方,我们平时用的真实电脑有 CPU 来处理数据、有内存来存储数据。JVM 这台 "小电脑" 也有类似的功能模块。它有自己的运行空间,里面划分出了不同的区域,就像真实电脑里的内存被划分成不同用途的区域一样。
当编写好 Java 程序后,java程序代码(.java)会被编译成一种 JVM 能理解的字节码文件(.class)。JVM 就负责运行这些字节码文件,它会把字节码翻译成计算机硬件能懂的指令,让程序在不同的操作系统上都能顺利运行。
而且,JVM 还能自动管理内存。比如,它会自动帮我们分配内存来存储程序运行时产生的数据,当这些数据不再使用时,JVM 还会自动把它们占用的内存回收掉,这样就不用担心内存不够用或者内存泄漏的问题。
所以说,JVM 就像是 Java 程序的一个 "大管家 ",为 Java 程序提供了一个统一的运行环境 ,让 Java 程序可以在不同的操作系统上都能稳定、高效地运行,实现了 "一次编写,到处运行" 的神奇效果。
1.2 JVM架构
**Java虚拟机(JVM)**是Java生态的核心引擎,通过将字节码转换为机器指令实现"一次编写,到处运行"。其架构包含三个核心子系统:
- 类加载器:负责加载.class文件
- 运行时数据区:内存管理核心区域
- 执行引擎:包含解释器和JIT编译器
JVM组件流程图
又比如我现在需要编译运行一个java程序 xiaoliang.java,那么这个java文件被运行的流程就如同下面这幅图:

总结
(1).java文件经过编译后变成 .class 字节码文件
(2)字节码文件通过类加载器 被搬运到 JVM 虚拟机中
(3)虚拟机主要的 5 大块:方法区,堆都为线程共享区域,有线程安全问题,栈和本地方法栈和计数器都是独享区域,不存在线程安全问题,而 JVM 的调优主要就是围绕堆,栈两大块进行
1.3 跨平台运行的本质
Java程序通过"中间层翻译"实现跨平台:
- .java源码 → .class字节码(二进制中间格式)
- JVM将字节码转换为本地机器指令(JIT编译优化)
- 不同平台的JVM实现负责对接操作系统API
二、类加载器
关于类加载器的详细介绍,大家可以通过传送门查看我的这篇文章:
1.1 类加载全过程
类加载生命周期
1.1.1 加载阶段
- 通过全限定名获取二进制字节流
- 生成方法区的运行时数据结构
- 创建对应的Class对象(作为访问入口)
1.1.2 验证阶段
- 文件格式验证(魔数CAFE BABE)
- 元数据验证(继承关系检查)
- 字节码验证(栈映射帧校验)
- 符号引用验证(常量池检查)
1.1.3 准备阶段
- 类变量(static变量)分配内存
- 设置初始值(0/false/null等)
- 示例:
public static int value = 123;
此时value=0
2.2 双亲委派机制
双亲委派模型执行流程
破坏双亲委派的典型场景:
- SPI机制(JDBC驱动加载)
- OSGi模块化
- 热部署实现
2.3 自定义类加载器
java
public class CustomClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) {
byte[] classData = loadClassData(name);
return defineClass(name, classData, 0, classData.length);
}
private byte[] loadClassData(String className) {
// 自定义加载逻辑
}
}
三、运行时数据区
3.1 堆内存结构
java
// 堆内存分配示例
byte[] data = new byte[10 * 1024 * 1024]; // 10MB数组直接进入老年代
3.1.1 新生代参数优化
java
-XX:NewRatio=2 # 老年代/新生代=2:1
-XX:SurvivorRatio=8 # Eden/Survivor=8:1:1
-XX:MaxTenuringThreshold=15 # 晋升阈值
3.1.2 内存分配策略
- TLAB(Thread Local Allocation Buffer)
- 逃逸分析与栈上分配
- 大对象直接进入老年代
3.2 虚拟机栈运行原理
栈帧结构详解:
java
public class StackFrameDemo {
public static void main(String[] args) {
int a = 1;
int b = 2;
int c = add(a, b);
}
private static int add(int x, int y) {
return x + y;
}
}
操作步骤 | 局部变量表 | 操作数栈 |
---|---|---|
iconst_1 | [ ] | [1] |
istore_1 | [a=1] | [ ] |
iconst_2 | [a=1] | [2] |
istore_2 | [a=1,b=2] | [ ] |
iload_1 | [a=1,b=2] | [1] |
iload_2 | [a=1,b=2] | [1,2] |
invoke | [...] | [...] |
2.3 方法区演进
JDK版本 | 实现方式 | 参数配置 | 特点 |
---|---|---|---|
≤1.6 | 永久代 | -XX:PermSize=64m | 受JVM内存限制 |
1.7 | 部分元数据 | -XX:PermSize=64m | 字符串常量池移至堆 |
≥1.8 | 元空间 | -XX:MetaspaceSize=256m | 使用本地内存,自动扩展 |
四、执行引擎深度解析
4.1 解释器与JIT协作

分层编译策略:
- 第0层:纯解释执行
- 第1层:C1简单编译(方法调用计数触发)
- 第2层:C2深度优化(回边计数触发)
4.2 JIT优化技术
4.2.1 方法内联优化
java
// 内联优化示例
public int add(int a, int b) {
return a + b;
}
// 调用处被优化为直接相加
int result = x + y;
4.2.2 逃逸分析优化
- 栈上分配(避免堆内存分配)
- 锁消除(线程私有对象去锁)
- 标量替换(分解对象为基本类型)
4.3 AOT编译实践
java
# 使用GraalVM编译原生镜像
native-image -H:+PrintAnalysisCallTree \
-H:+TraceClassInitialization \
-jar app.jar
五、垃圾回收全解析
5.1 对象存活判定算法
5.1.1 可达性分析算法
GC Roots类型:
- 虚拟机栈局部变量
- 方法区静态变量
- 方法区常量引用
- JNI全局引用
5.1.2 引用类型进阶
类型 | 回收条件 | 使用场景 |
---|---|---|
强引用 | 永不回收 | 普通对象 |
软引用 | 内存不足时回收 | 缓存 |
弱引用 | 下次GC时回收 | 缓存/监听 |
虚引用 | 随时可能回收 | 堆外内存管理 |
5.2 经典GC算法对比
算法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
标记-清除 | 简单快速 | 内存碎片 | 老年代 |
复制算法 | 无碎片 | 内存折半 | 新生代 |
标记-整理 | 内存紧凑 | 效率较低 | 老年代 |
分代收集 | 综合优势 | 实现复杂 | 现代JVM |
5.3 主流垃圾收集器
5.3.1 Parallel收集器家族
-
Parallel Scavenge :吞吐量优先
-
Parallel Old :老年代并行整理
-
参数配置示例:
java-XX:+UseParallelGC -XX:ParallelGCThreads=4 -XX:MaxGCPauseMillis=200
5.3.2 CMS收集器
-
四阶段过程 :
- 初始标记(STW)
- 并发标记
- 重新标记(STW)
- 并发清除
-
内存碎片问题解决方案:
java-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5
5.3.3 G1收集器革新
- Region分区模型:将堆划分为1MB~32MB区域
- SATB算法:Snapshot-At-The-Beginning
- 最佳实践参数:
java
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:InitiatingHeapOccupancyPercent=45
码字不易,希望可以一键三连,我们下期文章再见!!!