美文网首页
对象的创建

对象的创建

作者: 曾经不努力的人 | 来源:发表于2018-04-21 17:06 被阅读0次

    对象的创建

        虚拟机遇到一条new指令时,首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有那必须先执行类加载过程,类加载完成后会为新对象分配内存,对象所需的内存大小在类加载完成后已经确定,最后从Java堆中划出一段确定大小的内存。

        如果Java堆内存是规整的,一边区域是已分配的,一边区域是未分配的,那么可以通过移动指针完成内存的分配;如果Java堆内存是不规整的,那么Java虚拟机中需要维护一个内存可用的列表,然后从这个列表中选出一个足够大的内存片段进行分配,并更新列表记录。

        Java虚拟机创建对象是个很频繁的动作,在并发的情况下,即使移动一个指针也会涉及到线程安全的问题,有两种方法可以解决这个问题,一种是对内存分配的操作进行同步操作,另一种在Java堆中为每个线程分配一块内存,称为本地线程分配缓冲(TLAB)。哪个线程需要分配内存就在哪块TLAB上分配,当TLAB内存用完后,需要分配新的TLAB(这种情况下需要进行同步操作),通过-XX:+/-UseTLAB参数设置。

        内存分配完成后,虚拟机需要将分配的内存空间初始化为零值(不包括对象头)。这一步操作保证了对象的实例字段在Java代码中不赋值的情况下可以使用。

        接下来,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据、对象的哈希码、对象的GC分代年龄等信息,这些信息存在对象头中。

        上述这些步骤,从虚拟机的角度来看,一个对象已经产生了,但如果从Java程序来看,对象创建才刚刚开始,<init>方法还没执行,所有的字段都还是0。因此执行完new指令后会执行<init>方法,把对象按照程序员的意愿进行初始化。

    对象的内存布局

        此处主要分析的是HotSpot虚拟机。

        对象在内存中存储的布局分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

        对象头包括2部分信息:

        第一部分用于存储对象自身的运行时数据,包括哈希码(HashCode)、GC分代年龄、锁状态标识、线程持有的锁、偏向线程ID、偏向时间戳。

        第二部分是类型指针,对象指向它的类元数据的指针,即是这个对象是哪个类的实例。并不是所有的虚拟机实现都必须在对象数据中保留类型指针。如果对象是一个数组,还必须有一块用于记录数组长度的数据。因为普通对象可以通过元数据确定对象的大小,但数组中的元数据无法确定。

    对象的访问定位

        建立对象是为了使用对象,Java程序中需要通过栈上的reference引用使用对象,但具体怎么使用需要根据虚拟机来确定。

        通过句柄池引用:Java堆中有一块区域存储对象实例数据的句柄池,reference存储的是对象的句柄地址,句柄中包含了对象实例数据和对象类型的指针。

        通过直接指针访问:reference存储是对象的实例,对象的实例数据中包含了对象类型的指针。

        使用第一种方式,当对象移动后,只需要改变句柄池中对象的引用,不需要改变reference;使用第二种方式,减少了指针定位。

    相关文章

      网友评论

          本文标题:对象的创建

          本文链接:https://www.haomeiwen.com/subject/vtdxlftx.html