1. 为什么需要理解对象生命周期?
实际场景问题:内存泄漏、OOM异常、GC性能问题
核心价值:优化内存使用、避免内存泄漏、提升应用性能
主要内容概览:创建→使用→不可达→回收→内存重用
2. JVM内存结构回顾(简明版)
3. 对象生命周期的核心阶段
阶段1:创建(Creation)
类加载检查
遇到new指令时检查类是否已加载
未加载则执行类加载过程
内存分配
分配方式:
指针碰撞(堆内存规整)
空闲列表(堆内存不规整)
并发处理:
TLAB(Thread Local Allocation Buffer)
CAS+失败重试
内存空间初始化
初始化为零值(0/null/false)
设置对象头信息(Mark Word + Klass Pointer)
构造函数执行
<init>()
方法调用字段初始化、父类构造等
阶段2:使用(In Use)
可达性状态
强可达:通过GC Roots直接引用
软可达:仅被SoftReference引用
弱可达:仅被WeakReference引用
终结可达:重写了finalize()方法
虚可达:仅被PhantomReference引用
对象内存布局
|---------------------------------------------------|
| 对象头 (Header) |
| Mark Word (64 bits) |
| Klass Pointer (32 bits, 压缩后) |
|---------------------------------------------------|
| 实例数据 (Instance Data) |
| int id; // 4 bytes |
| String name; // 4 bytes (引用) |
| ... |
|---------------------------------------------------|
| 对齐填充 (Padding) // 按8字节对齐 |
|---------------------------------------------------|
访问定位方式
句柄访问(稳定,访问速度慢)
直接指针(HotSpot默认,访问速度快)
阶段3:不可达(Inaccessible)
可达性分析算法
GC Roots包括:
虚拟机栈中的局部变量
方法区中静态变量
方法区中常量引用的对象
本地方法栈JNI引用
同步锁持有的对象
两次标记过程
第一次标记:筛选需要执行finalize()的对象
第二次标记:真正回收前的最后检查
阶段4:回收(Reclamation)
垃圾收集算法
分代回收策略
新生代:Eden + Survivor0 + Survivor1
Minor GC触发条件:Eden区满
复制算法(存活对象移至Survivor区)
老年代:长期存活对象
Major GC触发条件:老年代空间不足
标记-清除/标记-整理算法
finalize()方法的特殊处理
执行时机不确定
可能造成对象"复活"
Java 9后已被弃用(示例说明原因)
阶段5:内存空间重用(Reuse)
回收后的内存加入空闲列表
新对象重用内存空间
内存压缩避免碎片化
4. 对象生命周期可视化流程
5. 影响生命周期的关键因素
1. 引用类型的作用
引用类型 | 强度 | GC行为 | 使用场景 |
---|---|---|---|
强引用 | 最强 | 永不回收 | 普通对象引用 |
软引用 | 中等 | 内存不足时回收 | 内存敏感缓存 |
弱引用 | 弱 | 发现即回收 | 防止内存泄漏 |
虚引用 | 最弱 | 跟踪对象回收 | 资源清理 |
2. 垃圾收集器的影响
Serial GC:单线程,适合客户端应用
Parallel GC:吞吐量优先
CMS:低延迟,标记-清除算法
G1:分区收集,可预测停顿
ZGC:TB级堆,<10ms停顿
3. 内存分配策略
对象优先在Eden分配
大对象直接进入老年代(-XX:PretenureSizeThreshold)
长期存活对象进入老年代(-XX:MaxTenuringThreshold)
动态年龄判断
6. 实战:生命周期监控与优化
1. 内存泄漏诊断
// 典型内存泄漏示例
public class MemoryLeak {
static List<Object> leakList = new ArrayList<>();
public static void main(String[] args) {
while (true) {
leakList.add(new byte[1024 * 1024]); // 持续添加大对象
try { Thread.sleep(100); }
catch (InterruptedException e) {}
}
}
}
2. 监控工具
jvisualvm:堆dump分析
jmap:内存快照
jmap -heap <pid> # 查看堆配置 jmap -histo:live <pid> # 对象直方图
jstat:实时GC监控
jstat -gcutil <pid> 1000 # 每秒输出GC情况
3. 优化策略
减少短命大对象创建
合理使用软/弱引用管理缓存
避免在循环中创建对象
及时清除过期引用
调整堆大小和分代比例
7. 特殊对象生命周期
1. 类对象生命周期
加载 → 验证 → 准备 → 解析 → 初始化
卸载条件苛刻(无实例、无ClassLoader引用等)
2. 字符串常量池
字面量创建的特殊处理
intern()方法的作用与风险
3. 匿名类与Lambda
捕获外部变量时的生命周期绑定
方法区存储vs堆存储
8. 总结与最佳实践
关键总结:
对象生命周期由GC Roots可达性决定
不同引用类型提供精细控制能力
分代收集优化GC效率
最佳实践:
避免使用finalize()
大对象生命周期单独管理
监控老年代增长趋势
根据应用特点选择GC算法