JVM-02 对象创建

69 阅读3分钟

对象的创建

new 一个对象时先检测类对象是否在常量池存在,不存在则先进行相应的类加载过程。检查通过后虚拟机将为对象分配内存.

  • 设Java堆中内存是绝对规整的,所有被使用过的内存都被放在一边,空闲的内存被放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间方向挪动一段与对象大小相等的距离,这种分配方式称为“指针碰撞”(Bump The Pointer)。

  • 但如果Java堆中的内存并不是规整的,已被使用的内存和空闲的内存相互交错在一起,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表”(Free List)。

  • 选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有空间压缩整理(Compact)的能力决定。

内存分配完成之后,虚拟机必须将分配到的内存空间(但不包括对象头)都初始化为零值。至此虚拟机层面的对象已经创建完成,但java方面还需要进行init()构造。

对象的内存布局

对象在堆内存中的存储布局可以划分为三个部分:

  • 对象头(Header)
  • 实例数据(Instance Data)
  • 对齐填充(Padding)

对象头

对象头里的信息是与对象自身定义的数据无关的额外存储成本

对象头部分包括两类信息。

  • 第一类是用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32个比特和64个比特,官方称它为“Mark Word”。

  • 对象头的另外一部分是类型指针,即对象指向它的类型元数据(class)的指针,通过该指针确定是哪个class的实例对象。

如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,

实例数据

HotSpot虚拟机默认的分配顺序为longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers,OOPs)。 从以上默认的分配策略中可以看到,相同宽度的字段总是被分配到一起存 放,在满足这个前提条件的情况下,在父类中定义的变量会出现在子类之前。

如果HotSpot虚拟机的+XX:CompactFields参数值为true(默认就为true),那子类之中较窄的变量也允许插入父类变量的空隙之中,以节省出一点点空间。

对象访问定位

主流的访问方式主要有使用句柄直接指针两种:

使用句柄的最大好处就是reference中存储的是稳定句柄地 址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而 reference本身不需要被修改。

GW.png

使用直接指针来访问最大的好处就是速度更快,它节省了一次指针定位的时间开销,HotSpot主要使用的就是直接指针 5Z2T.png