浅析JVM对象的创建过程、内存布局以及访问定位

一、对象的创建过程

  关于对象的创建,第一反应是 new 关键字。Student stu =new Student("**","18");就拿这句代码来说:

1、虚拟机首先会去检查Student这个类有没有被加载,如果没有,首先去加载这个类到方法区。

2、然后根据加载的Class类对象创建stu实例对象。

3、需要注意的是,stu对象所需的内存大小在 Student类加载完成后便可完全确定。

4、内存分配完成后,虚拟机需要将分配到的内存空间的实例数据部分初始化为零值,这也就是为什么我们在编写Java代码时创建一个变量不需要初始化。

5、紧接着,虚拟机会对对象的对象头进行必要的设置,如这个对象属于哪个类,如何找到类的元数据(Class对象)、对象的锁信息、GC分代年龄等。

6、设置完对象头信息后,调用类的构造函数。

  其实讲实话,虚拟机创建对象的过程远不止这么简单,我这里只是把大致的脉络讲解了一下,方便大家理解。

二、对象的内存布局

  刚刚提到的实例数据,对象头,有些小伙伴也许有点陌生,这一小节就详细讲解一下对象的内存布局。

  对象创建完成后大致可以分为以下几个部分:

1、对象头:对象头中包含了对象运行时一些必要的信息,如GC分代信息,锁信息,哈希码,指向Class类元信息的指针等。

  其中对开发者比较有用的是锁信息与指向Class对象的指针

  关于指向Class对象的指针其实很好理解。比如上面那个Student的例子,当我们拿到stu对象时,调用Class stuClass=stu.getClass(); 的时候,其实就是根据这个指针去拿到了stu对象所属的Student类在方法区存放的Class类对象。

2、实例数据:实例数据部分是对象真正存储的有效信息,就是程序代码中所定义的各种类型的字段内容。

3、对齐填充:虚拟机规范要求对象大小必须是8字节的整数倍。

  对齐填充其实就是来补全对象大小的。

三、对象的访问定位

  谈到对象的访问,还拿上面学生的例子来说,当我们拿到stu对象时,直接调用stu.getName();时,其实就完成了对对象的访问。

  但这里要说一下的是,stu虽然通常被认为是一个对象,其实准确来说是不准确的,stu只是一个变量,变量里存储的是指向对象的指针,(如果干过C或者C++的小伙伴应该比较清楚指针这个概念),当我们调用stu.getName()时,虚拟机会根据指针找到堆里面的对象然后拿到实例数据name。

  需要注意的是,当我们调用stu.getClass()时,虚拟机会首先根据stu指针定位到堆里面的对象,然后根据对象头里面存储的指向Class类元信息的指针再次到方法区拿到Class对象,进行了两次指针寻找。具体讲解图如下:

 

posted @ 2021-09-14 18:41  古兰精  阅读(82)  评论(0编辑  收藏  举报