深入理解java虚拟机(2)——HotSpot虚拟机对象探秘

1、对象的创建

 

本小节讨论普通对象,不包括数组和Class对象。

 

当遇到一条new指令时,有如下操作:

  • 检查常量池中是否有这个对象所属类的符号引用;

    • 若常量池中没有这个类的符号引用,说明类没有被定义!抛出ClassNotException;

    • 若常量池中有这个类的符号引用,则进行下一步工作;

 

  • 进而检查这个符号引用代表的类是否被JVM加载、解析和初始化;

    • 如果类没有被加载,则找到该类的Class文件,并加载到方法区;

    • 若该类已经被JVM加载,则准备为对象分配内存;

 

  • 根据方法区该类的信息确定该类所需内存大小;

        对象所需内存的大小在类加载完之后就可完全确定;一个类产生的每个对象的内存大小是一样的!

 

  • 从堆中分配一块对应大小的内存空间给新的对象;

            分配堆中内存有两种方式:

                        1)指针碰撞

                            适用于java 堆内存规整的情况,如:JVM的GC器采用复制算法或标记-整理算法,那么堆中空闲内存是完整的区域,并且空闲内存和已使用内存由一个指针标记。当为一个对象分配内存,只需移动指针即可。

                        2)空闲列表

                           如果JVM的垃圾回收器使用标记-清除算法,那么堆中空闲区域和已使用区域交错,因此需要维护一个“空闲列表”来记录堆中哪些区域是空闲区域,从而在分配对象内存时,可以通过空闲列表找到空闲区域,分配内存。

                           综上:采用何种分配方式,取决于它使用了何种垃圾回收器。

 

            除了划分可用空间外,在并发情况下,并不是线程安全的。如:正在为对象A分配内存,指针还没来得及修改,对象B又用了原来的指针分配内存。两种解决方案:

                        1)对分配内存空间的动作进行同步处理----JVM采用CAS配上失败重试的方式保证更新操作的原子性;

                        2)每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(TLAB)。哪个线程要分配内存,就在哪个线程的TLAB上分配,只有在TLAB用完并分配新的TLAB,才需要同步锁定。-XX:+/-UseTLAB参数来设定是否使用TLAB。          

 

  • 为对象中的成员变量初始化为零值(不包括对象头)

  • 设置对象头中的信息

  • 调用对象的构造函数进行初始化

        此时,整个对象的创建过程就完成了。

 

2、对象的内存布局

 

对象在内存的存储布局分为3块区域:

  • 对象头

  • 实例数据

  • 对齐填充

 

2.1 对象头

 

包含两部分信息:

  • 对象在运行过程中所需要的一些数据:HashCode、GC分代年龄、锁状态标记、线程持有的锁、偏向线程ID、偏向时间戳等。

  • 可能还包含类型指针,即对象指向它的类元数据的指针。通过这个指针JVM可以确定对象属于哪个类。

    如果对象是一个数组,对象头还要包含数组长度。

 

2.2 实例数据

 

实例数据是对象真正存储的有效信息---成员变量的值,其中包含父类的成员变量和本类的成员变量。

 

2.3 对齐补充

 

并不是必然存在的,起着占位符的作用。用于确保对象的总长度是8字节的整倍数。

HotSpot VM要求对象的其实地址必须是8字节的整数倍,即对象大小必须是8字节的整数倍(对象头正好是8字节的整数倍,所以可能要对实例数据进行填充)。

 

3、对象的访问

 

Java程序是通过栈上的reference数据来操作具体对象,那么这个reference是如何去定位对象的?有两种对象访问方式:

  • 句柄访问

          需要在堆中划分一块“句柄池”,用于存放所有对象的地址和所有对象所属类的类信息。

          引用类型的变量存放的是该对象在句柄池的地址。访问对象时,首先需要通过引用类型的变量找到该对象的句柄,然后根据句柄中的对象地址再访问对象。

  • 直接指针访问

        引用变量存放的是对象的地址,从而直接通过引用能访问对象。

        但对象所在内存空间需要额外的策略存储对象所属类信息的地址。

 

比较:

 

  • 句柄的优势:引用变量存储的是稳定的句柄地址。在对象移动时(在gc中非常普遍)只会改变句柄中的实例数据指针,而reference本身不需要修改。

  • 直接指针的优势:速度快。只需一次寻址操作。对象访问在Java中非常频繁,这类开销节省是非常可观的

 

HotSpot采用的是直接指针方式访问对象。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值