JVM(5)——对象的生命周期

发布于:2025-06-19 ⋅ 阅读:(17) ⋅ 点赞:(0)

1. 为什么需要理解对象生命周期?

  • 实际场景问题:内存泄漏、OOM异常、GC性能问题

  • 核心价值:优化内存使用、避免内存泄漏、提升应用性能

  • 主要内容概览:创建→使用→不可达→回收→内存重用

2. JVM内存结构回顾(简明版)

3. 对象生命周期的核心阶段

阶段1:创建(Creation)
  1. 类加载检查

    • 遇到new指令时检查类是否已加载

    • 未加载则执行类加载过程

  2. 内存分配

    • 分配方式

      • 指针碰撞(堆内存规整)

      • 空闲列表(堆内存不规整)

    • 并发处理

      • TLAB(Thread Local Allocation Buffer)

      • CAS+失败重试

  3. 内存空间初始化

    • 初始化为零值(0/null/false)

    • 设置对象头信息(Mark Word + Klass Pointer)

  4. 构造函数执行

    • <init>()方法调用

    • 字段初始化、父类构造等

阶段2:使用(In Use)
  1. 可达性状态

    • 强可达:通过GC Roots直接引用

    • 软可达:仅被SoftReference引用

    • 弱可达:仅被WeakReference引用

    • 终结可达:重写了finalize()方法

    • 虚可达:仅被PhantomReference引用

  2. 对象内存布局

|---------------------------------------------------|
| 对象头 (Header)                                   |
|   Mark Word (64 bits)                             |
|   Klass Pointer (32 bits, 压缩后)                 |
|---------------------------------------------------|
| 实例数据 (Instance Data)                          |
|   int id; // 4 bytes                              |
|   String name; // 4 bytes (引用)                  |
|   ...                                             |
|---------------------------------------------------|
| 对齐填充 (Padding) // 按8字节对齐                 |
|---------------------------------------------------|
  1. 访问定位方式

    • 句柄访问(稳定,访问速度慢)

    • 直接指针(HotSpot默认,访问速度快)

阶段3:不可达(Inaccessible)
  1. 可达性分析算法

    • GC Roots包括:

      • 虚拟机栈中的局部变量

      • 方法区中静态变量

      • 方法区中常量引用的对象

      • 本地方法栈JNI引用

      • 同步锁持有的对象

  2. 两次标记过程

    • 第一次标记:筛选需要执行finalize()的对象

    • 第二次标记:真正回收前的最后检查

阶段4:回收(Reclamation)
  1. 垃圾收集算法

  1. 分代回收策略

    • 新生代:Eden + Survivor0 + Survivor1

      • Minor GC触发条件:Eden区满

      • 复制算法(存活对象移至Survivor区)

    • 老年代:长期存活对象

      • Major GC触发条件:老年代空间不足

      • 标记-清除/标记-整理算法

  2. 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. 监控工具
  1. jvisualvm:堆dump分析

  2. jmap:内存快照

    jmap -heap <pid>        # 查看堆配置
    jmap -histo:live <pid>  # 对象直方图

  3. jstat:实时GC监控

    jstat -gcutil <pid> 1000  # 每秒输出GC情况

3. 优化策略
  • 减少短命大对象创建

  • 合理使用软/弱引用管理缓存

  • 避免在循环中创建对象

  • 及时清除过期引用

  • 调整堆大小和分代比例

7. 特殊对象生命周期

1. 类对象生命周期
  • 加载 → 验证 → 准备 → 解析 → 初始化

  • 卸载条件苛刻(无实例、无ClassLoader引用等)

2. 字符串常量池
  • 字面量创建的特殊处理

  • intern()方法的作用与风险

3. 匿名类与Lambda
  • 捕获外部变量时的生命周期绑定

  • 方法区存储vs堆存储

8. 总结与最佳实践

  1. 关键总结

    • 对象生命周期由GC Roots可达性决定

    • 不同引用类型提供精细控制能力

    • 分代收集优化GC效率

  2. 最佳实践

    • 避免使用finalize()

    • 大对象生命周期单独管理

    • 监控老年代增长趋势

    • 根据应用特点选择GC算法


网站公告

今日签到

点亮在社区的每一天
去签到