深入理解Java虚拟机——对象探秘

437 阅读5分钟

在《深入理解Java虚拟机》第二章讲解了自动内存管理机制,在Java内存结构之后是对象的创建、内存布局、访问定位,但是在对象的创建过程中会涉及到第七章类加载机制,为此后续在总结类加载机制时再细品类的创建过程,此刻我们仅仅在Java内存结的基础上分析对象的创建、内存布局、访问定位;

对象的创建过程

  • 从上图可大致看出类的创建过程,在对象的创建过程中Jvm会给对象在堆内存中分配空间,分配空间的方式分为以下二种解决方案:

    1、指针碰撞:要求堆中内存绝对规整,所有用过的内存都放一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,分配内存就仅仅只是将该指针向空闲空间那边挪动一段与对象大小相等的距离;

    2、空闲列表:堆中内存不规整,虚拟机维护着一个列表,记录哪些内存块是可用的,在分配时从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录;

  • 堆内存是否规整取决于垃圾收集器是否带有压缩整理功能;

  • 在为对象分配内存时,还需要考虑的一点就是线程安全性问题。可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况。针对这种问题,有以下两种解决方案

    1、对分配内存空间的动作进行同步处理,保证更新操作的原子性(采用CAS + 失败重试机制保障原原子性),但效率较低;

    2、使用本地线程分配缓冲(TLAB),哪个线程需要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并需要分配新的TLAB时,才需要同步锁定(可通过-XX:+/-UseTLAB参数来设定虚拟机启用TLAB);

对象的内存布局

在HotSpot虚拟机中,对象在内存中存储的布局可分为3块区域:对象头、实例数据、对齐填充;

  • 对象头由二部分组成:MarkWork、类型指针

    1、(MarkWork)存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有锁、偏向线程ID、偏向时间戳等;

    2、类型指针是虚拟机通过这个指针来确定对象是哪个类的实例,该指针指向对象的类元数据;(由于查找对象的元数据信息并不一定要经过对象本身,所以并不是所有的虚拟机实现都必须在对象数据上保留类型指针);

    3、如果对象是一个JAVA数组,那么在对象头中还必须有一块用于记录数组长度的数据,因为普通对象的大小可通过元数据信息来获取,而数组不行;

  • 实例数据

    1、存储对象有效信息,也就是在代码中所定义的各种类型的字段内容,无论是从父类继承下来的,还是在子类中定义的,都需要记录起来;

    2、存储顺序受虚拟机分配策略参数(FieldsAllocationStyle)和字段在java源码中定义顺序的影响;

    3、HotSpot虚拟机默认的分配策略为longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers),相同带宽的字段总是被分配到一起,在此之后,父类中定义的变量会出现在子类之前,如果CompactFields参数的值为true(默认为true),那么子类中较窄的变量也可能会插入到父类变量的空隙中;

  • 对齐填充

    这部分不是必然存在的,没有什么特别的含义,仅仅起占位符的作用,因为要求对象起始地址必须是8字节的整数倍,因此当对象实例数据部分没有对齐时,就需要通过对齐填充来补全;

对象的访问定位

注:对象实例的数据放在java堆中,对象类型的数据放在方法区;

建立对象就是为了使用对象,Java程序需要通过栈上的reference数据来操作堆上的具体的对象,对象的访问定位方式有二种:

  • 句柄访问

    Java 堆会划分一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息;

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

  • 直接指针访问

    使用直接指针访问,java堆中对象的布局就必须考虑如何放置访问类型数据的相关信息,而reference中存储的直接就是对象地址;

优点:使用直接指针访问方式的最大好处就是速度更快,它节省了一次指针定位的时间开销,由于对象的访问在 Java 中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本;

致谢

鄙人不才,在您面前献丑只愿与您结伴而行,文章若有不当之处,望大佬指点一二;如果我对您有帮助的话,还希望您能点赞分享,成长是一场苦涩的独自修行,我很需要您的陪伴与支持,这也是不断前行的根本动力,让我们在互相陪伴见证彼此生长的同时能够感染身边最亲近的人一同成长,在此叩谢!