Java堆中的对象分配、布局和访问

1. 对象创建:

  通常情况下,对于程序中的创建新对象的指令,如new A();
  首先jvm要检查类A是否已经被加载到了内存,如果还没有,需要先触发类的加载、解析、初始化。然后在堆上创建对象,在jvm中分为如下几步:
  1. 在堆中分配相应大小的空间(空间大小怎么计算的,接下来说)
  2. 完成实例数据部分的初始化工作(初始化为0值)
  3. 完成对象头的填充:如对象自身的运行时数据、类型指针等。
  至此,从jvm的角度,完成了对象的初始化,但在用户角度,初始化才正式开始,开始调用<init>方法完成初始复制和构造函数。

              这里写图片描述

  在第1步中,jvm要为对象分配合适大小的空间,但是垃圾回收算法的不同,堆内的可用空间结构也会不一样,这取决于堆内可用空间是否规整:如果是规整的,可以采用简单的定界指针指示空闲区域的开头,将定界指针向空闲区域移动对象所需空间即完成了对象的内存分配(这种方法称为“指针碰撞”)。如果不是规整的,则需要采用空闲列表的方式。

< 对照上图,第2步中还没有填充header信息如类型指针,这需要谁来填充呢?按道理来说jvm是可以完成这部分工作的,因为jvm是按照相应的Class对象来创建对象实例的,应该知道类型。 >


2. 对象的内存布局:

  如上图所示,对象状态相关信息主要包括,hashCode,GC分代年龄,锁状态标志,线程持有的锁,偏向锁等等。32bit 或 64bit。对象类型信息不是一定要包含的,这取决于对象内存访问方式:
  对采用对象句柄的方式,对象引用相当于一个二级指针,指向一个对象的句柄,对象的句柄又分为两部分:指向对象实例的指针和指向对象类型数据的指针,在这种方式中,对象内部是可以不含有类型信息的。
  在采用直接指针的方式中才需要含有类型指针。
  对齐填充是为了使对象的大小满足为8字节的整数倍的要求。

  而所有的类类型数据还有方法,都是在类加载过程中就完成的,其信息也是存储在方法区中的Class对象内。
  这里需要注意的是,要将对象的上述行为和对应的类的加载过程,还有执行时的过程区分开来,并且要清楚每个阶段分别完成了哪些工作。

类的加载过程

简要回顾一下:
类的加载主要完成从.class文件的字节流到方法区中的类运行时数据结构Class的建立以及初始化。
包括加载、验证、准备、解析、初始化的过程可能是交错的,准备阶段主要是在方法区中为类变量分配内存,然后赋0值,解析阶段主要是将常量池中的符号引用替换为直接引用的过程,包括类和接口的符号引用、字段的符号引用、方法的符号引用等,替换为内存中实际项目的 入口地址。

如果说,我将类、接口、方法的符号引用认为是对应在方法区中的入口地址或者方法字段的入口地址的话,那么怎么理解字段的符号引用?比如

class A{
    private static int m;
    private int n;
    private B b;
    private static C c;
}

那么在常量池中肯定有类似 m 、n、b、c的符号引用,那对m、c分别解析为什么呢?
对n、b又分别解析为什么呢?(还是说现在不解析,比如b、n此时根本解析不了)

最后是初始化:<cinit>通过收集所有的类变量赋值动作和静态语句块,来初始化。并且还会保证父类的<cinit>先执行。

方法的调用过程

解析调用:对于invokestatic和invokespecial两个指令(还有final方法),会直接去寻找符号引用对应的直接地址。在解析阶段就确定了的。

  分派调用:可能是静态的也可能是动态的。对于方法的重载来说,主要是根据方法的参数来确定调用的版本,根据静态类型就可以确定,因此在编译期就可以确定,并将对应的符号引用和调用指令放在一起。属于静态多分派。
  对方法的重写来说,是根据方法的实际接收者的入口地址,并通过多态查找过程确定的最终的调用版本。是动态单分派的。
  为了提高多态查找的效率,在实际的类型信息中维持有虚方法表,保存实际的入口地址。

总结来说,我觉得关键在于invokestatic和invokespecial,与invokevirtual相比,没有多态查找过程,也没有事先要压栈实际对象的需求。

关于invokeDynamic觉得很有意思,有空研究一下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值