JavaMemoryModel
Java 内存管理手册
—— 从运行时结构到垃圾回收深度解析
Java 内存管理
区域 | 存储内容 | 生命周期 | 线程共享性 |
---|---|---|---|
方法区(Method Area) | 类信息、常量、静态变量 | JVM 启动到关闭 | 共享 |
堆(Heap) | 对象实例、数组 | 对象创建到垃圾回收 | 共享 |
虚拟机栈(VM Stack) | 方法调用的栈帧(局部变量、操作数栈) | 方法调用开始到结束 | 线程私有 |
本地方法栈(Native Stack) | JNI(本地方法)调用 | 本地方法执行期间 | 线程私有 |
程序计数器(PC Register) | 当前线程执行的字节码行号 | 线程创建到销毁 | 线程私有 |
1. Java 内存模型(JMM)概述
Java 内存管理由 JVM(Java Virtual Machine) 自动处理,开发者无需手动分配释放内存,但仍需理解其机制以避免内存泄漏或性能问题。
核心目标:
✅ 高效分配对象内存
✅ 自动回收无用对象(GC)
✅ 隔离线程安全(栈私有 vs 堆共享)
2. 运行时数据区(Runtime Data Areas)
JVM 内存划分为以下核心区域:
(1) 方法区(Method Area)
- 存储内容:
- 类元信息(类名、父类、接口、方法代码等)。
- 运行时常量池(如
String
常量、final
静态变量)。
- 关键点:
- JDK 8 后由 元空间(Metaspace) 实现(取代永久代),使用本地内存,默认无上限(需监控防溢出)。
- 溢出错误:
OutOfMemoryError Metaspace
。
(2) 堆(Heap)
- 存储内容:所有对象实例和数组。
- 分代设计(优化 GC 效率):
- 新生代(Young Generation):新创建的对象。
- Eden 区:对象首次分配处。
- Survivor 区(S0S1):Minor GC 后存活的对象。
- 老年代(Old Generation):长期存活的对象(经过多次 GC 后晋升)。
- 永久代(PermGen,JDK 7 及之前):已废弃,被元空间取代。
- 新生代(Young Generation):新创建的对象。
- 溢出错误:
OutOfMemoryError Java heap space
。
(3) 虚拟机栈(VM Stack)
- 存储内容:每个线程私有的栈帧(Stack Frame),包含:
- 局部变量表(基本类型、对象引用)。
- 操作数栈(方法计算时的临时数据)。
- 动态链接(指向方法区的方法引用)。
- 方法返回地址。
- 栈深度问题:
- 默认栈大小 1MB(可通过
-Xss
调整)。 - 溢出错误:
StackOverflowError
(如无限递归)。
- 默认栈大小 1MB(可通过
(4) 本地方法栈(Native Method Stack)
- 为 JNI(本地方法,如 CC++ 代码)服务的栈空间。
- 溢出错误:同虚拟机栈。
(5) 程序计数器(PC Register)
- 记录当前线程执行的字节码行号(线程私有)。
- 唯一无溢出风险的区域。
3. 垃圾回收(Garbage Collection, GC)
(1) 对象存活判定
- 引用计数法(已淘汰):循环引用无法处理。
- 可达性分析(主流):
- GC Roots 包括:
- 虚拟机栈中的局部变量。
- 方法区中的静态变量。
- JNI 引用的本地对象。
- 从 GC Roots 出发,不可达的对象标记为垃圾。
- GC Roots 包括:
(2) 垃圾回收算法
算法 原理 适用场景 优缺点
标记-清除 标记垃圾 → 直接清除 老年代 简单,但产生内存碎片
复制算法 将存活对象复制到另一块内存 新生代(Eden→S0S1) 无碎片,但浪费 50% 空间
标记-整理 标记垃圾 → 存活对象向一端移动 → 清除 老年代 无碎片,但移动对象耗时
分代收集 结合上述算法,按对象生命周期分代处理 所有区域 综合性能最优(HotSpot 默认)
(3) 垃圾收集器(HotSpot 实现)
收集器 分代 算法 特点
Serial 新生代 复制 单线程,STW(Stop-The-World)
Parallel Scavenge 新生代 复制 多线程,吞吐量优先
CMS 老年代 标记-清除 并发收集,低延迟(JDK 14 移除)
G1 全堆 分Region+标记-整理 平衡吞吐与延迟(JDK 9+ 默认)
ZGC 全堆 染色指针 超低延迟(JDK 15+ 生产可用)
(4) GC 调优参数
# 堆大小设置
-Xms512m -Xmx1024m # 初始堆 512MB,最大堆 1GB
-XXNewRatio=2 # 新生代老年代 = 12
-XXSurvivorRatio=8 # EdenS0S1 = 811
# 选择收集器
-XX+UseG1GC # 启用 G1
-XX+UseConcMarkSweepGC # 启用 CMS(已废弃)
4. 内存问题诊断与工具
(1) 常见内存问题
- 内存泄漏:对象无用时仍被引用(如静态集合未清理)。
- 内存溢出:对象过多或堆设置过小(
OutOfMemoryError
)。 - GC 过频:新生代太小导致频繁 Minor GC。
(2) 诊断工具
工具 用途 命令用法
jstat 监控 GC 统计信息 jstat -gcutil pid 1000
jmap 生成堆转储快照(Heap Dump) jmap -dumpformat=b,file=heap.hprof pid
VisualVM 图形化分析内存和线程 集成于 JDK,支持插件扩展
MAT(Eclipse) 分析 Heap Dump 查找泄漏对象 加载 .hprof
文件
Arthas 在线诊断 JVM 问题(如监控方法调用) watch com.example.MyClass
5. 实战:内存优化案例
案例 1:缓存泄漏
问题:静态 HashMap
缓存数据未清理,导致老年代堆积。
解决:改用弱引用 WeakHashMap
或定期清理。
案例 2:频繁 Full GC
现象:System.gc()
被滥用或元空间未限制。
优化:禁用显式 GC(-XX+DisableExplicitGC
)并限制元空间(-XXMaxMetaspaceSize=256m
)。
案例 3:线程栈溢出
场景:递归调用未设终止条件。
修复:改为循环或增加栈大小(-Xss2m
)。
6. 总结
主题 关键点
内存结构 堆存对象、栈存方法调用,方法区存类信息,PC 寄存器记录执行位置。
GC 机制 分代收集(新生代复制算法,老年代标记-整理),G1ZGC 为现代低延迟选择。
问题排查 结合 jstat
、Heap Dump、MAT 定位泄漏或溢出。
调优原则 避免过早优化,监控后再调整堆大小、分代比例和收集器。
附录:
- Java GC Tuning Guide
- 《深入理解 Java 虚拟机》——周志明
通过本手册,您可系统掌握 Java 内存管理的核心知识,高效应对生产环境问题! 🚀