当前位置: 首页 > news >正文

JVM对象创建与内存分配机制深度剖析

📌 JVM对象创建与内存分配机制深度剖析


一、对象创建流程
  1. 类加载检查

    • 执行new指令时,检查常量池能否定位到类的符号引用。
    • 若类未加载,先触发类加载过程(加载→验证→准备→解析→初始化)。
    • 对应场景:new关键字、对象克隆、对象序列化。
  2. 分配内存

    • 划分内存方法
      • 指针碰撞(Bump the Pointer)
        堆内存规整时,移动指针划分连续空间(默认方式)。
      • 空闲列表(Free List)
        堆内存不规整时,从空闲列表查找可用内存块。
    • 解决并发问题
      • CAS + 重试:保证原子性。
      • TLAB(线程本地分配缓冲)
        为每个线程预分配私有内存区域(参数:-XX:+UseTLAB默认开启,-XX:TLABSize设置大小)。
  3. 初始化

    • 内存空间置零(排除对象头),若启用TLAB则提前初始化。
    • 保证字段不赋初值可直接使用(默认零值:int=0boolean=false等)。
  4. 设置对象头(Object Header)

    • 存储内容
      • Mark Word:运行时数据(哈希码、GC分代年龄、锁状态等)。
      • Klass Pointer:指向类元数据的指针。
    • 内存布局
      区域说明大小(64位系统)
      Mark Word哈希码、锁状态、GC年龄等8字节
      Klass Pointer类元数据指针(压缩后)4字节(默认开启压缩)
      数组长度(可选)仅数组对象存在4字节
    • 锁状态标记示例
      锁状态锁标志位其他存储内容
      无锁01哈希码、分代年龄
      偏向锁01线程ID、Epoch、分代年龄
      轻量级锁00指向栈中锁记录的指针
      重量级锁10指向互斥量的指针
      GC标记11
  5. 执行<init>方法

    • 按程序员意图初始化对象(显式赋初值、执行构造方法)。

二、对象大小与指针压缩
  1. 测量工具

    <dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.9</version>
    </dependency>
    
    ClassLayout layout = ClassLayout.parseInstance(new Object());
    System.out.println(layout.toPrintable());
    
  2. 指针压缩(Compressed Oops)

    • 启用参数-XX:+UseCompressedOops(默认开启),禁用:-XX:-UseCompressedOops
    • 作用
      • 64位系统下用32位指针,减少内存占用(降低约1.5倍)。
      • 支持最大堆内存 32GB(超过时指针压缩失效,需用64位地址)。
    • 场景
      • 堆内存 < 4GB:无需压缩。
      • 堆内存 ≤ 32GB:启用压缩。

三、对象内存分配策略
  1. 栈上分配

    • 条件
      • 开启逃逸分析(-XX:+DoEscapeAnalysis,默认开启)。
      • 开启标量替换(-XX:+EliminateAllocations,默认开启)。
    • 优化场景:对象未逃逸出方法作用域(如方法内临时对象)。
  2. Eden区分配

    • 对象优先在Eden区分配,空间不足触发 Minor GC
    • Survivor区分配
      • 默认比例 Eden : Survivor = 8:1:1
      • 关闭比例自适应:-XX:-UseAdaptiveSizePolicy
  3. 大对象直入老年代

    • 参数-XX:PretenureSizeThreshold=字节大小(仅Serial/ParNew收集器有效)。
    • 目的:避免大对象复制开销。
  4. 长期存活对象晋升老年代

    • 年龄计数器:每熬过1次Minor GC年龄+1。
    • 晋升阈值
      • 默认 -XX:MaxTenuringThreshold=15(CMS收集器默认为6)。
  5. 动态年龄判断

    • Survivor区中同年龄对象总大小 > Survivor区50%时,≥该年龄的对象晋升老年代。
    • 参数-XX:TargetSurvivorRatio(指定占比阈值)。
  6. 老年代空间分配担保

    • 流程
      1. Minor GC前检查老年代剩余空间是否 > 年轻代对象总大小。
      2. 若不足,检查 -XX:-HandlePromotionFailure 参数。
      3. 老年代空间 < 历次晋升平均大小,触发 Full GC

四、对象内存回收
  1. 判断对象死亡

    • 引用计数法:存在循环引用问题(JVM未采用)。
    • 可达性分析法:从GC Roots(栈局部变量、静态变量等)向下扫描引用链。
  2. 引用类型

    类型特点
    强引用普遍对象引用(Object obj = new Object()
    软引用SoftReference包裹,内存不足时回收(适合缓存)
    弱引用WeakReference包裹,下次GC必回收
    虚引用PhantomReference包裹,用于对象回收跟踪(极少用)
  3. finalize() 自救机制

    • 对象首次标记后,若重写finalize()且重新关联引用链,可避免回收(仅一次机会)。

五、类卸载条件

同时满足以下三点视为无用类:

  1. 堆中不存在该类的任何实例。
  2. 加载该类的 ClassLoader 已被回收。
  3. 该类对应的 java.lang.Class 对象未被引用,无法通过反射访问。

:关键参数总结

  • 指针压缩:-XX:+UseCompressedOops
  • 逃逸分析:-XX:+DoEscapeAnalysis
  • 标量替换:-XX:+EliminateAllocations
  • 年龄阈值:-XX:MaxTenuringThreshold=15
  • 大对象阈值:-XX:PretenureSizeThreshold=1000000

此笔记完整覆盖对象生命周期(创建→分配→回收),可直接用于CSDN博文发布。**以下是根据文档内容整理的详细笔记,已优化为Markdown格式(适用于CSDN编辑器):


📌 JVM对象创建与内存分配机制深度剖析


一、对象创建流程
  1. 类加载检查

    • 执行new指令时,检查常量池能否定位到类的符号引用。
    • 若类未加载,先触发类加载过程(加载→验证→准备→解析→初始化)。
    • 对应场景:new关键字、对象克隆、对象序列化。
  2. 分配内存

    • 划分内存方法
      • 指针碰撞(Bump the Pointer)
        堆内存规整时,移动指针划分连续空间(默认方式)。
      • 空闲列表(Free List)
        堆内存不规整时,从空闲列表查找可用内存块。
    • 解决并发问题
      • CAS + 重试:保证原子性。
      • TLAB(线程本地分配缓冲)
        为每个线程预分配私有内存区域(参数:-XX:+UseTLAB默认开启,-XX:TLABSize设置大小)。
  3. 初始化

    • 内存空间置零(排除对象头),若启用TLAB则提前初始化。
    • 保证字段不赋初值可直接使用(默认零值:int=0boolean=false等)。
  4. 设置对象头(Object Header)

    • 存储内容
      • Mark Word:运行时数据(哈希码、GC分代年龄、锁状态等)。
      • Klass Pointer:指向类元数据的指针。
    • 内存布局
      区域说明大小(64位系统)
      Mark Word哈希码、锁状态、GC年龄等8字节
      Klass Pointer类元数据指针(压缩后)4字节(默认开启压缩)
      数组长度(可选)仅数组对象存在4字节
    • 锁状态标记示例
      锁状态锁标志位其他存储内容
      无锁01哈希码、分代年龄
      偏向锁01线程ID、Epoch、分代年龄
      轻量级锁00指向栈中锁记录的指针
      重量级锁10指向互斥量的指针
      GC标记11
  5. 执行<init>方法

    • 按程序员意图初始化对象(显式赋初值、执行构造方法)。

二、对象大小与指针压缩
  1. 测量工具

    <dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.9</version>
    </dependency>
    
    ClassLayout layout = ClassLayout.parseInstance(new Object());
    System.out.println(layout.toPrintable());
    
  2. 指针压缩(Compressed Oops)

    • 启用参数-XX:+UseCompressedOops(默认开启),禁用:-XX:-UseCompressedOops
    • 作用
      • 64位系统下用32位指针,减少内存占用(降低约1.5倍)。
      • 支持最大堆内存 32GB(超过时指针压缩失效,需用64位地址)。
    • 场景
      • 堆内存 < 4GB:无需压缩。
      • 堆内存 ≤ 32GB:启用压缩。

三、对象内存分配策略
  1. 栈上分配

    • 条件
      • 开启逃逸分析(-XX:+DoEscapeAnalysis,默认开启)。
      • 开启标量替换(-XX:+EliminateAllocations,默认开启)。
    • 优化场景:对象未逃逸出方法作用域(如方法内临时对象)。
  2. Eden区分配

    • 对象优先在Eden区分配,空间不足触发 Minor GC
    • Survivor区分配
      • 默认比例 Eden : Survivor = 8:1:1
      • 关闭比例自适应:-XX:-UseAdaptiveSizePolicy
  3. 大对象直入老年代

    • 参数-XX:PretenureSizeThreshold=字节大小(仅Serial/ParNew收集器有效)。
    • 目的:避免大对象复制开销。
  4. 长期存活对象晋升老年代

    • 年龄计数器:每熬过1次Minor GC年龄+1。
    • 晋升阈值
      • 默认 -XX:MaxTenuringThreshold=15(CMS收集器默认为6)。
  5. 动态年龄判断

    • Survivor区中同年龄对象总大小 > Survivor区50%时,≥该年龄的对象晋升老年代。
    • 参数-XX:TargetSurvivorRatio(指定占比阈值)。
  6. 老年代空间分配担保

    • 流程
      1. Minor GC前检查老年代剩余空间是否 > 年轻代对象总大小。
      2. 若不足,检查 -XX:-HandlePromotionFailure 参数。
      3. 老年代空间 < 历次晋升平均大小,触发 Full GC

四、对象内存回收
  1. 判断对象死亡

    • 引用计数法:存在循环引用问题(JVM未采用)。
    • 可达性分析法:从GC Roots(栈局部变量、静态变量等)向下扫描引用链。
  2. 引用类型

    类型特点
    强引用普遍对象引用(Object obj = new Object()
    软引用SoftReference包裹,内存不足时回收(适合缓存)
    弱引用WeakReference包裹,下次GC必回收
    虚引用PhantomReference包裹,用于对象回收跟踪(极少用)
  3. finalize() 自救机制

    • 对象首次标记后,若重写finalize()且重新关联引用链,可避免回收(仅一次机会)。

五、类卸载条件

同时满足以下三点视为无用类:

  1. 堆中不存在该类的任何实例。
  2. 加载该类的 ClassLoader 已被回收。
  3. 该类对应的 java.lang.Class 对象未被引用,无法通过反射访问。

:关键参数总结

  • 指针压缩:-XX:+UseCompressedOops
  • 逃逸分析:-XX:+DoEscapeAnalysis
  • 标量替换:-XX:+EliminateAllocations
  • 年龄阈值:-XX:MaxTenuringThreshold=15
  • 大对象阈值:-XX:PretenureSizeThreshold=1000000
http://www.lryc.cn/news/604492.html

相关文章:

  • 基于Catboost的铁路交通数据分析及列车延误预测系统的设计与实现【全国城市可选、欠采样技术】
  • 【JVM篇11】:分代回收与GC回收范围的分类详解
  • 数据分析师进阶——95页零售相关数据分析【附全文阅读】
  • JVM 性能调优实战:让系统性能 “飞” 起来的核心策略
  • 观远 ChatBI 完成 DeepSeek-R1 大模型适配:开启智能数据分析跃升新篇
  • 【Spring】一文了解SpringMVC的核心功能及工作流程,以及核心组件及注解
  • Linux 日志管理与时钟同步详解
  • GIS工程师面试题
  • GitHub 热门项目 PandaWiki:零门槛搭建智能漏洞库,支持 10 + 大模型接入
  • UG NX二次开发(Python)-根据封闭曲线创建拉伸特征
  • Class27GoogLeNet
  • 实用性方案:高效处理图片拼接的正确打开方式
  • sed编程入门
  • [Agent开发平台] Coze Loop开源 | 前端 | typescript架构API速查
  • Python Pandas.get_dummies函数解析与实战教程
  • 【iOS】weak修饰符
  • 磁盘io查看命令iostat与网络连接查看命令netstat
  • [Qt]QString 与Sqlite3 字符串互动[汉字不乱码]
  • iOS电池寿命与App能耗监测实战 构建完整性能监控系统
  • 常见CMS获取webshell的方法-靶场练习
  • 2025年自动化工程与计算机网络国际会议(ICAECN 2025)
  • C++菱形虚拟继承:解开钻石继承的魔咒
  • 3D空间中的变换矩阵
  • 应用药品 GMP 证书识别技术,实现证书信息的自动化、精准化提取与核验
  • Jupyter Notebook安装使用
  • React 开发中遇见的低级错误
  • 防止飞书重复回调通知分布式锁
  • 从单体到分布式:解锁架构进化密码
  • 基于定制开发开源AI智能名片S2B2C商城小程序的B站私域流量引流策略研究
  • day25——HTML CSS 前端开发