
Java 平台无关性
编译与优化
JIT 优化
- 逃逸分析
- 若对象未逃逸出方法作用域,可能被优化为栈上分配(减少堆内存分配开销)。
AOT 编译
- 提前编译
- 在编译期将字节码直接编译为机器码,生成平台相关的二进制文件。
运行时内存组成
| 区域 | 作用 | 线程共享性 |
|---|---|---|
| 程序计数器 | 记录当前线程执行的字节码指令地址(线程私有)。 | 私有 |
| Java 虚拟机栈 | 存储局部变量表、操作数栈、动态链接等(线程私有)。 | 私有 |
| 本地方法栈 | 为本地(Native)方法服务(线程私有)。 | 私有 |
| 堆 | 存储对象实例,是垃圾回收(GC)的主要区域。 | 共享 |
| 方法区 | 存储已加载的类信息、常量、静态变量、JIT 编译后的代码等(Java 8 后为元空间)。 | 共享 |
方法区实现方式
- 永久代(Java 7 及之前)
- 元空间(Java 8+,直接使用本地内存)
- 堆(部分实现可能使用堆内存)
Class 常量池
- 作用:存储编译期生成的字面量和符号引用。
- 加载流程:类加载时解析并存入运行时常量池。
运行时常量池
- 包含字面量和符号引用,是类加载后的动态常量池。
字符串常量池
- 特性:不可变,存储字符串字面量的唯一实例。
- 来源:
- 字面量在编译期进入 Class 常量池,运行期进入 字符串常量池。
- 运行期通过
String.intern()手动添加字符串到池中。
intern()方法逻辑:- 若池中已存在相同字符串,返回池中的引用;否则将字符串加入池并返回新引用。
对象存储模型(OOP-Klass)
对象内存布局
- 对象头
- 存储 MarkWord(锁状态)、Klass 指针(指向类元数据)等。
- 实例数据
- 对象的字段值(包括父类字段)。
- 对齐填充
- 填充字节以保证内存对齐(提升访问效率)。
对象创建流程
- 类加载检查
- 检查常量池是否能定位到类的符号引用,是否已加载。
- 内存分配
- 指针碰撞法(内存规整时)或 空闲列表法(内存不规整时)。
- 初始化零值
- 将对象字段初始化为默认值(如
int为0,引用为null)。
- 将对象字段初始化为默认值(如
- 设置对象头
- 填充 MarkWord 和 Klass 指针。
- 执行构造方法
- 调用
<init>方法初始化对象。
- 调用
- 返回对象引用
TLAB 技术(Thread-Local Allocation Buffer)
- 作用:为每个线程分配独立的堆内存区域(TLAB),避免多线程分配时的竞争。
- 替代方案:CAS + 失败重试(性能较低)。
垃圾回收(GC)
算法分类
| 算法 | 特点 | 缺点 |
|---|---|---|
| 标记-清除 | 直接标记并清除垃圾对象。 | 内存碎片化 |
| 标记-复制 | 将内存分为两块,存活对象复制到另一块,清空原块。 | 浪费 50% 内存 |
| 标记-整理 | 标记存活对象并移动到内存一端,整理碎片。 | 耗时较长 |
GC 的 STW(Stop-The-World)
- 问题:垃圾回收时会暂停所有应用线程。
- 优化:通过并发标记、三色标记法减少 STW 时间。
对象存活判断
- 引用计数法
- 缺点:无法处理循环引用(如两个对象互相引用但无外部引用)。
- 可达性分析法
- GC Roots:
- 系统类加载器加载的类。
- 活着的线程栈中的局部变量。
- 被
synchronized锁定的对象。 - JNI 引用的对象。
- 从 GC Roots 出发,遍历引用链,不可达的对象判定为可回收。
- GC Roots:
三色标记法
- 阶段:
- 初始标记(STW):标记 GC Roots 直接引用的对象。
- 并发标记:从灰色对象(已标记但未扫描子引用)开始遍历,与用户线程并行。
- 重新标记(STW):修正并发标记期间变动的引用(如新增或删除引用)。
- 漏标问题解决:
- 增量更新:记录并发期间新增的引用。
- 原始快照:基于标记开始时的引用关系快照。
引用类型
| 类型 | 回收时机 | 典型应用 |
|---|---|---|
| 强引用 | 默认引用类型,只要存在强引用,对象不会被回收。 | 普通对象赋值 |
| 软引用 | 内存不足时优先回收软引用指向的对象。 | 缓存实现 |
| 弱引用 | 只能存活到下一次 GC 前,无论内存是否充足。 | ThreadLocal |
| 虚引用 | 无法通过虚引用获取对象实例,仅用于跟踪对象被回收的状态(需配合 ReferenceQueue)。 | 对象回收状态监控 |
垃圾回收器分类
按线程模型
- 串行单线程
Serial GC(新生代)、Serial Old(老年代)。
- 并行多线程(关注吞吐量)
Parallel New(新生代)、Parallel Old(老年代)。
- 并发(关注停顿时间)
CMS(老年代,标记-清除算法)。G1(整堆,标记-复制 + 标记-整理)。ZGC(整堆,低停顿、高吞吐)。
CMS 回收流程
- 初始标记(STW):标记 GC Roots 直接关联的对象。
- 并发标记:与用户线程并行标记存活对象。
- 重新标记(STW):修正并发标记期间的变动。
- 并发清除:与用户线程并行清理垃圾对象。
- Full GC(单线程):当碎片过多或老年代不足时触发。
G1 回收流程
- Young GC(STW):回收 Eden + Survivor 区。
- 并发标记:标记存活对象。
- Mixed GC(STW):回收部分老年代 + 新生代。
- Full GC(单线程,尽量避免):内存不足时触发。
- 特点:将堆划分为多个 Region,动态调整新生代/老年代比例。
新生代与老年代
内存划分
- 新生代:存放新分配的对象,分为
Eden+ 2 个Survivor区(S0/S1)。 - 老年代:存放长期存活的对象(如经过多次 GC 仍存活的对象)。
Java 8 GC 行为
- 新生代:标记-复制算法。
- 老年代:标记-整理算法。
- 对象分配流程:
- 新对象优先分配到
Eden。 Eden快满时触发Young GC:- 扫描
Eden和Survivor0,存活对象复制到Survivor1。 - 清空
Eden和Survivor0。
- 扫描
- 下次 GC 时交换
Survivor0和Survivor1角色。
- 新对象优先分配到
- 跨代引用问题:
- Remembered Set:记录老年代对象指向新生代对象的引用,避免全堆扫描。
类生命周期
加载阶段
- 加载:将
.class文件读入 JVM。 - 链接:
- 验证:确保字节码合法。
- 准备:为静态变量分配内存并赋默认值。
- 解析:将符号引用转换为直接引用。
- 初始化:执行静态变量赋值和静态代码块(
<clinit>方法)。
使用与卸载
- 使用:类实例化、调用方法等。
- 卸载:满足条件时由 JVM 卸载类(如无实例、无引用)。
懒加载
- 触发条件:
- 创建类的实例。
- 访问类的静态变量或方法。
- 反射调用类。
- 基础类:JVM 启动时自动加载(如
java.lang.Object)。
类加载器
分类
- 启动类加载器(C++ 实现,JVM 内部组件)。
- 其他类加载器(Java 实现,独立于 JVM)。
双亲委派模型
- 类加载请求先委派给父类加载器。
- 父类加载器无法加载时,子类加载器尝试加载。
- 目的:避免重复加载,保证核心类安全(如防止用户篡改
java.lang.String)。
JVM 调优工具与参数
工具
- 命令行工具:
jps:查看 JVM 进程状态。jstack:分析线程堆栈(死锁检测)。jmap:生成堆转储快照(分析内存泄漏)。jstat:监控 JVM 统计信息(GC、类加载等)。
- 可视化工具:
jconsole、VisualVM:图形化监控堆内存、线程、GC 等。
关键参数
| 类别 | 参数示例 | 作用 |
|---|---|---|
| 堆内存 | -Xms(初始堆大小)、-Xmx(最大堆大小) | 控制堆内存范围 |
| 栈内存 | -Xss(线程栈大小) | 设置每个线程的栈容量 |
| GC 选择 | -XX:+UseG1GC(启用 G1)、-XX:+UseParallelGC(启用并行 GC) | 指定垃圾回收器 |
| 元空间 | -XX:MetaspaceSize(初始大小)、-XX:MaxMetaspaceSize(最大大小) | 替代永久代的内存管理 |
| 调试信息 | -XX:+PrintGCDetails(打印 GC 详情)、-verbose:gc(输出 GC 日志) | 记录 GC 行为 |
| OOM 处理 | -XX:+HeapDumpOnOutOfMemoryError(内存溢出时生成堆转储) | 辅助分析内存泄漏 |
深入解析JAVA虚拟机(JVM)原理与优化
1134

被折叠的 条评论
为什么被折叠?



