对象实例化内存布局和访问定位

对象实例化的几种方式

  1. new
    1. 变形一:Xxx的静态方法获取(比如说单例模式)
    2. 变形二:XxxBuilder/XxxFactory的静态方法(工厂模式)
  2. Class的newInstance():反射的方式,只能调用空参构造方法,权限必须是public
  3. Constructor的newInstance(Xxx):反射的方式,可以调用空参、有参构造方法,权限没有要求
  4. 使用clone():不调用任何构造器,需要当前类实现Cloneeable接口,实现clone()
  5. 使用反序列化:从文件、网络中获取一个对象的二进制流
  6. 第三方库Objenesis

从字节码看对象的创建步骤

  1. 检查对象对于的类是否加载、链接、初始化(涉及到类的父类的加载)
  2. 给对象分配内存空间:具体使用那种看使用的垃圾收集器所采用的算法
    1. 内存规整:指针碰撞
    2. 内存规整:需要维护一张列表,记录空闲的内存空间。空闲列表分配
  3. 处理并发安全问题
    1. 采用CAS失败重试,区域加锁保证更新的原子性
    2. 每个线程预先分配了一块TLAB
  4. 初始化分配到的空间:对对象的属性进行默认的初始化,设置属性类型的默认初始化值
  5. 设置对象头
  6. 执行<init>方法,显示初始化属性的值

对象内存布局

  1. 对象头
    1. 运行时元数据:如果创建的是一个数组,还要记录数组的长度
      1. 哈希值(HashCode)
      2. GC分代年龄
      3. 锁状态标志
      4. 线程持有的锁
      5. 偏向线程ID
      6. 偏向时间戳
    2. 类型指针:指向类元数据InstanceKlass,确定对象所属的类型
  2. 实例数据
    1. 对象真正存储的有效信息,包括程序代码中定义的各种类型的字段(包括从父类继承下来的和自己本身所拥有的)
    2. 规则
      1. 相同宽度(大小)的字段总是分配到一起
      2. 父类定义的变量会出现在子类之前
      3. 如果CompactFieIds的参数为true(默认为true):子类的窄变量有可能插入到父类变量的空隙
  3. 对齐填充:起到占位符的作用
    1. 由于HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是任何对象的大小都必须是8字节的整数倍。对象头部分已经被精心设计成正好是8字节的倍数(1倍或者2倍),因此,如果对象实例数据部分没有对齐的话,就需要通过对齐填充来补全

对象的定位访问

有两种方式:句柄访问、直接指针(HotSpot采用)

句柄访问

        局部变量表保存指向堆中的句柄池的地址,由句柄分别指向实例池中的对象实例和方法区中的对象类型数据

直接指针

优缺点:

  1. 句柄访问
    1. 优点:如果对象的地址发送改变(比如空间整理),局部变量表保存的句柄不需要变化
    2. 缺点:占用堆内存空间,并且相对于直接指针多了一步
  2. 直接指针
    1. 优点:不占用堆内存空间,效率比句柄快
    2. 如果对象的地址发送改变,局部变量表中的值需要改变

图释

 再补一张JVM内存布局

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值