Java面试之JVM(5)对象的实例化内存布局与访问定位

以下内容来自网络整理,侵删
Student s = new Student(); 在内存中做了哪些事?
  • 加载Student.class 文件进内存
  • 在栈内存为s开辟空间
  • 为学生对象分配内存
  • 初始化分配到的空间,成员变量设置默认值
  • 设置对象头信息
  • 执行构造方法对学生对象的成员变量赋值
  • 学生对象初始化完毕,把对象地址赋值给s变量
对象的四种创建方式
  • 类加载检查。判断这个对象对应的类是否已经加载、解析、初始化过。
  • 为对象分配内存
    • 如果内存规整,那么虚拟机将采取指针碰撞法(把指针向空闲空间那边挪动一段与对象大小相等的距离。)来为对象分配内存。
      (像Serial,ParNew这种基于标记-整理算法的垃圾收集器,虚拟机采用这种方式分配内存)
    • 如果内存不规整,虚拟机将采用空闲列表法为对象分配内存
      (使用标记-清除算法的CMS垃圾收集器)
    • 并发安全:方式1,采用CAS失败重试、区域加锁保证更新的原子性;方式2,内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)
      (意思是虚拟机维护了一个列表,记录哪些内存块是可用的,再分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表的内容)
  • 初始化分配到的空间,所有属性初始化为零值,保证对象实例字段在不赋值时可以直接使用
  • 设置对象头信息
  • 执行构造方法进行初始化
对象什么时候不在堆中分配
  • 逃逸: 逃逸是指在某个方法之内创建的对象,还在方法体之外被其它变量引用到;这样带来的后果是在该方法执行完毕之后,该方法中创建的对象将无法被GC回收。比如有方法逃逸、线程逃逸。
  • 开启逃逸分析后,如果对象的作用域仅在方法内,那么对象可能创建在虚拟机栈上面,随方法入栈创建,出栈销毁,减小垃圾回收压力
对象的内存布局

包含三部分:对象头(header),实例数据(instance data)、对齐填充(padding)

  • 对象头。包含两部分:运行时数据、类型指针
    • Mark Word(标记字段)。包括:哈希吗(hashcode)、GC分代年龄、锁状态标志、线程持有锁、偏向线程ID、偏向时间戳(这部分与synchronized底层实现原理有关)
    • 类元数据的指针,通过这个指针可以知道,对象是哪个类的实例。如果是数组,需要记录数组长度。注意:访问通过直接指针才会这样。句柄访问,不会有类型指针。
  • 实例数据
    • 存储对象的真正的数据。父类中的数据也会记录在这里。相同宽度的字段优先分配到一起。
  • 对齐填充。
    • 保证对象的大小是8字节的整数倍,占位符的作用
对象的访问定位

在这里插入图片描述
两种方法:句柄、直接指针

  • 句柄。Java堆中对应一块内存—句柄池。对象的引用指向句柄池,句柄指向堆中的实例和方法区中的类数据。
    在这里插入图片描述
  • 直接指针。对象的引用直接指向了堆中的实例。实例里面存储了类数据
    在这里插入图片描述
    • 句柄优点:移动对象不需要修改对象的引用,只需要修改句柄的内容。
    • 缺点:需要两次定位
    • 直接指针优点:快,只需要一次定位。
    • 缺点:对象移动,需要修改对象的引用。
    • Hotspot虚拟机实现,默认使用直接指针。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值