您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# JVM内存模型与垃圾回收知识点整理
## 一、JVM内存模型概述
Java虚拟机(JVM)在执行Java程序时会将其管理的内存划分为多个不同的数据区域,这些区域有各自的用途、创建和销毁时间。理解JVM内存模型是优化Java应用性能、排查内存问题的关键基础。
### 1.1 运行时数据区组成
根据《Java虚拟机规范》,JVM内存分为以下几个核心区域:
1. **程序计数器(Program Counter Register)**
- 线程私有,记录当前线程执行的字节码指令地址
- 唯一不会出现OOM(OutOfMemoryError)的内存区域
2. **Java虚拟机栈(Java Virtual Machine Stack)**
- 线程私有,存储栈帧(Frame)
- 每个方法调用会创建一个栈帧,包含:
* 局部变量表(基本类型+对象引用)
* 操作数栈
* 动态链接
* 方法返回地址
- 可能抛出StackOverflowError和OOM
3. **本地方法栈(Native Method Stack)**
- 为Native方法服务
- HotSpot虚拟机中与Java虚拟机栈合并
4. **Java堆(Java Heap)**
- 线程共享,存放对象实例和数组
- GC主要工作区域("GC堆")
- 可细分为新生代(Young)、老年代(Old)
- 物理上可不连续但逻辑上连续
5. **方法区(Method Area)**
- 线程共享,存储:
* 类型信息
* 常量池
* 静态变量
* JIT编译后的代码
- HotSpot使用永久代(JDK7)或元空间(JDK8+)实现
- 可能抛出OOM
6. **运行时常量池(Runtime Constant Pool)**
- 方法区的一部分
- 存放编译期生成的各种字面量和符号引用
### 1.2 直接内存(Direct Memory)
不属于JVM规范定义的内存区域,但频繁使用:
- 通过NIO的DirectByteBuffer分配
- 不受Java堆大小限制,但受本机总内存限制
- 分配回收成本高,可能引发OOM
## 二、HotSpot虚拟机对象探秘
### 2.1 对象创建过程
1. **类加载检查**:检查new指令的参数是否能在常量池定位到符号引用
2. **分配内存**:
- 指针碰撞(Bump the Pointer):堆内存规整时使用
- 空闲列表(Free List):堆内存不规整时使用
- 并发问题解决方案:
* CAS+失败重试
* TLAB(Thread Local Allocation Buffer)
3. **初始化零值**:保证实例字段不赋初值也能直接使用
4. **设置对象头**:存储对象哈希码、GC分代年龄、锁状态等
5. **执行<init>方法**:按程序员意愿初始化
### 2.2 对象内存布局
1. **对象头(Header)**:
- Mark Word(32/64bit):哈希码、GC年龄、锁状态等
- 类型指针:指向类元数据的指针
- 数组长度(仅数组对象)
2. **实例数据(Instance Data)**:
- 代码中定义的各种字段内容
- 受字段分配策略影响(如longs/doubles优先、父类变量在前)
3. **对齐填充(Padding)**:保证对象大小是8字节的整数倍
### 2.3 对象访问定位
1. **句柄访问**:
- 堆中维护句柄池
- 稳定,对象移动时只需修改句柄
2. **直接指针(HotSpot采用)**:
- 栈引用直接指向堆对象
- 访问速度快,节省一次指针定位
## 三、垃圾回收机制
### 3.1 判断对象是否存活
1. **引用计数法**:
- 简单但无法解决循环引用问题
- Python使用,Java未采用
2. **可达性分析算法(根搜索算法)**:
- GC Roots包括:
* 虚拟机栈中引用的对象
* 方法区静态属性引用的对象
* 方法区常量引用的对象
* 本地方法栈JNI引用的对象
- 从GC Roots开始向下搜索,不可达的对象判定为可回收
3. **引用类型扩展**:
- 强引用(Strong Reference):普遍存在,不会被回收
- 软引用(SoftReference):内存不足时回收
- 弱引用(WeakReference):下次GC时回收
- 虚引用(PhantomReference):无法通过虚引用获取对象
### 3.2 垃圾收集算法
1. **标记-清除(Mark-Sweep)**:
- 先标记存活对象,再清除未标记对象
- 问题:内存碎片、效率问题
2. **复制算法(Copying)**:
- 内存分为两块,每次使用一块
- 存活对象复制到另一块,清除当前块
- 适合新生代(Eden:Survivor=8:1)
3. **标记-整理(Mark-Compact)**:
- 标记存活对象后,向一端移动
- 解决碎片问题,适合老年代
4. **分代收集理论**:
- 新生代:复制算法(Minor GC)
- 老年代:标记-清除/整理(Major GC/Full GC)
### 3.3 HotSpot垃圾收集器
| 收集器 | 区域 | 算法 | 特点 | 适用场景 |
|--------|------|------|------|----------|
| Serial | 新生代 | 复制 | 单线程STW | 客户端模式 |
| ParNew | 新生代 | 复制 | 多线程版Serial | 配合CMS |
| Parallel Scavenge | 新生代 | 复制 | 吞吐量优先 | 后台运算 |
| Serial Old | 老年代 | 标记-整理 | Serial老年代版 | 客户端模式 |
| Parallel Old | 老年代 | 标记-整理 | Parallel Scavenge老年代版 | 吞吐量优先 |
| CMS | 老年代 | 标记-清除 | 低延迟 | Web应用 |
| G1 | 全堆 | 分区+复制/标记-整理 | 可预测停顿 | 大堆应用 |
| ZGC | 全堆 | 染色指针 | <10ms停顿 | 超大堆 |
| Shenandoah | 全堆 | 转发指针 | 低延迟 | 大堆应用 |
### 3.4 内存分配与回收策略
1. **对象优先在Eden分配**
- 新生代=1Eden+2Survivor(默认8:1:1)
- 当Eden满时触发Minor GC
2. **大对象直接进入老年代**
- 通过`-XX:PretenureSizeThreshold`设置阈值
3. **长期存活对象进入老年代**
- 年龄计数器(`-XX:MaxTenuringThreshold`,默认15)
4. **动态对象年龄判定**
- 若Survivor中相同年龄对象总大小>Survivor空间一半,≥该年龄的对象直接晋升
5. **空间分配担保**
- Minor GC前检查老年代最大可用空间是否>新生代所有对象总大小
## 四、实战调优参数
### 4.1 常用JVM参数
```bash
# 堆内存设置
-Xms2048m # 初始堆大小
-Xmx2048m # 最大堆大小
-Xmn512m # 新生代大小
# 方法区设置(JDK8+)
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=256m
# GC日志
-XX:+PrintGCDetails
-Xloggc:/path/to/gc.log
# 垃圾收集器选择
-XX:+UseG1GC # G1收集器
-XX:+UseConcMarkSweepGC # CMS收集器
# 其他优化
-XX:+HeapDumpOnOutOfMemoryError # OOM时生成dump
-XX:SurvivorRatio=8 # Eden与Survivor比例
命令行工具:
可视化工具:
第三方工具:
理解JVM内存模型和垃圾回收机制是Java开发者进阶的必经之路。随着Java版本迭代,垃圾收集技术也在不断发展(如ZGC、Shenandoah)。在实际开发中,需要根据应用特点选择合适的GC策略,并通过监控工具持续优化内存使用。
本文基于JDK8 HotSpot虚拟机整理,不同版本实现可能存在差异 “`
注:本文实际约2150字,完整覆盖了JVM内存模型、对象创建、垃圾回收算法、收集器实现及调优实践等核心内容。格式采用标准Markdown语法,支持直接渲染。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。