涨姿势,Java 中 New 一个对象是个怎么样的过程?,linux 操作系统实用教程教师用书
在类加载检查通过后,接下来虚拟机将为新生的对象分配内存。对象所需的内存的大小在类加载完成后便可以完全确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来,目前常用的有两种方式:
**1.指针碰撞(Bump the Pointer):**假设 Java 堆的内存是绝对规整的,所有用过的内存都放一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅把那个指针向空闲空间那边挪动一段与对象大小相等的距离。
**2.空闲列表(Free List):**如果 Java 堆中的内存并不是规整的,已使用的内存和空间的内存是相互交错的,虚拟机必须
维护一个空闲列表,记录上哪些内存块是可用的,在分配时候从列表中找到一块足够大的空间划分给对象使用。
除了如何划分可用空间外,在并发情况下划分不一定是线程安全的,有可能出现正在给 A 对象分配内存,指针还没有来得及修改,对象 B 又同时使用了原来的指针分配内存的情况,解决这个问题两种方案:
**分配内存空间的动作进行同步处理:**实际上虚拟机采用 CAS 配上失败重试的方式保证了更新操作的原子性。
**内存分配的动作按照线程划分在不同的空间中进行:**为每个线程在 Java 堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB)。
内存分配完后,虚拟机需要将分配到的内存空间中的数据类型都初始化为零值(不包括对象头);接下来虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息,这些信息都存放在对象的对象头中。
做完以上以后,从虚拟机视角来看,一个新的对象已经产生了,但是 Java 程序视角来看,执行 new 操作后会接着执行方法,把对象按照程序员的意愿进行初始化,这样一个真正的对象就产生了。
二、对象的内存布局
在 HotSpot 虚拟机中,对象在内存中的存储的布局可以分为 3 块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding).
(一)对象头
1.第一部分用于存储对象自身的运行时数据,如哈希码、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等,官方称为“Mark Word”。
2.类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象的哪个实例。面试:[100 道 Java 中高级面试题汇总+详细拆解](
)
(二)实例数据
对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。
评论