JVM虚拟机笔记之对象探究(二)

1 对象的创建

  • 1.虚拟机遇到new指令时首先去检查这个指令的参数能否在常量池中定位到一个类的符号引用,并且检查引用代表的类是否已被加载、解析和初始化过。如果没有,则执行类加载过程(第7章
    虚拟机类加载机制)。
  • 2.加载检查通过后,分配内存(内存在类加载完成后便可完全确定)。
  • 3.内存分配完成后,虚拟机对对象进行必要的设置,如对象是哪个类的实例、如何找到类的元数据信息.对象哈希码.对象的GC分代年龄等(都放在对象的对象头中)。

内存分配方式有:指针碰撞和空闲列表。使用哪种方式取决于堆是否规整(又由垃圾回收决定)。
内存分配的线程安全问题:1.CAS配上失败重试的方式保证原子性;2.每个线程在Java堆中预先分配一小块内存,本地线程分配缓冲(TLAB)。分配完成后内存空间初始化为0值(不包括对象头)。

从虚拟机角度看,一个新的对象产生了,但从java程序视角看,对象创建才刚刚开始,因为<init>方法还没有执行,,所有字段为零。执行new指令之后会接着执行<init>方法(构造方法),进行初始化,这样一个真正可用的对象才算完成产生。

2 对象的内存布局

对象在内存中存储的布局可以分为3块区域:对象头、实例数据、对齐填充

①对象头含两部分(Header)

  • 存储对象自身的运行时数据,如哈希码、GC分代年龄.锁信息(锁标志,线程持有的锁,偏向线程ID)等。长度在32位和64位的虚拟机中,分别为32bit、
    64bit,官方称它为“Mark Word”。
  • 类型指针,对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

备注:如果对象是java数组,对象头中还必须有一块记录数据长度的数据

②实例数据(InstanceData)

  • 对象真正存储的有用信息,也是程序中定义的各种类型的字段内容,包括父类继承的都记录下来,存储顺序受到虚拟机分配策略参数和字段在Java源码中的顺序的影响。

③对齐填充(Padding)

  • 由于HotSpot虚拟机要求对象的起始地址必须是8字节的整数倍,通俗的说,就是对象大小必须是8字节的整数倍。对象头正好是8字节的倍数。当实例数据部分没有对齐时,需要通过对齐填充来补全。

3 对象的访问定位

Java程序通过栈上的reference数据来操作堆上的具体对象。
不同虚拟机实现的对象访问方式会有所不同,目前主流的访问方式有两种:使用句柄和直接指针。

  • 使用句柄:堆中划分出一块内存作为句柄池,reference存储对象的句柄地址 是间接访问,优点是reference中存储的是稳定的句柄地址,对象移动(垃圾会收时)时只会改变句柄中的实例数据指针。

JVM虚拟机笔记之对象探究(二)

  • 使用直接指针 是直接访问,节省了一次指针定位的开销,优点就是速度快。(HotSpot)

JVM虚拟机笔记之对象探究(二)

注:对象类型数据指针(类对象引用)在Java堆里面。(个人观点:是类对象引用在编译器存在于常量池,而常量池是在堆里面)

相关推荐