JAVA虚拟机(三) ---- 对象的创建

上一章讲了 java虚拟机的内存区域的划分 ,本章来学习一下,对象的创建过程。

开始之前先看一下,方法区的存放数据结构,方法区存放的是已被虚拟机加载的类信息。常量,静态变量,即时编译器编译后的代码,对于HotSpot虚拟机来说,方法区也被很多开发者称为 “永久代”,但是本质上他们并不等价。

在这里插入图片描述

下面是对象创建的过程,以及对象的存储结构 ,本次描述只针对正常创建对象的过程

在这里插入图片描述
1、当虚拟机遇到 Student stu = new Student(); 时,检索到关键字 new 明白需要创建一个对象。

2、首先从方法区中的常量池中检索Student类的符号引用,并检查Student类是否被 加载、解析和初始化,如果没有执行类加载过程(以后章节在讲,现在知道有这么一回事就行)

3、虚拟机开始为对象在堆内存中分配一个固定大小的空间(对象的大小,在类加载时已经确定),分配空间时有两种方式:一是“指针碰撞”,二是“空闲列表

指针碰撞:
假设java虚拟机中的堆内存是规整的,已经使用过的内存放在一边,空闲的处于另一边,中间存放一个指针作为中间分界点的指示器,分配内存时,仅将指针往空闲方法移动等同对象大小的距离,这种方式叫做-------“指针碰撞”
空闲列表:
假设虚拟机中的堆内存不是规整的,已使用的内存和空闲内存相互交叉,没有规律可言,这个时候也就不能使用简单的指针碰撞方式分配内存,虚拟机必须维护一个列表,记录那些内存是未使用的,在分配内存的时候,从列表中找到一个足够大小的内存分配出去,同时更新列表,这种方式叫做------“空闲列表”

4、现在堆内存存在Student()对象了,虚拟机开始为对象设置零值(不包括对象头),即设置对象的属性的 ‘初始值’,假设Student类有两个字段,name和age 这两个属性,虚拟机设置零值的时候,将设置(String) name = null ,(int) age = 0,这就是程序没有设置初始值,我们却能直接使用,访问到的也就是虚拟机为对象设置的零值。

5、接下来虚拟机将设置对象的一些必要信息,比如:这个对象是哪个类的实例、对象的哈希码(HashCode)、对象的GC分代年龄等信息,这些信息是放在对象头 (Object Header) 中的

6、上面的操作执行完之后,对虚拟机来说一个对象就已经创建好了,对于java程序来说,对象创建才刚刚开始 ------ init 方法还没有执行,所有的字段都还是零值,执行 init 方法程序按照开发人员的意愿初始化,这个对象才真正的创建完成。

… …

对象的创建过程基本基本结束了,下面来详细看看第5步,虚拟机是如何对对象设置一些必要信息
对象的结构信息(对象头 Header,实例数据 Instance Data,对齐补充 Padding):

~ 在这里插入图片描述

对象头:
对象头存储分成两部分:一是存储自身运行时数据,哈希码,GC分代年龄等,二是存储自身类型指针,即对象指向他的类元数据指针,虚拟机通过这个指针来确定对象是哪个类的具体实例,但这不是一定需要的,查找对象的类元数据信息,并不一定是通过对象本身的,如果对象是数组的话还要一块来记录数组长度信息。

实例数据:
实例数据存储的是对象真正有效的数据,也就是程序定义的各个字段,无论是从父类继承下来的,还是子类的,都需要记录下来。

对齐补充:
这一部分的不是必要存在的,仅仅起一个占位符的作用,HotSpot 虚拟机内存管理系统规定了对象的起始地址必须是8字节的整数倍,也就是说对象的大小必须是8字节的整数倍,而对象头正好是8字节的整数倍,如果实例数据部分不是8字节的整数倍,那么就会通过对齐补充来补足对象的大小,使其成为8字节的整数倍。

最后谈一谈对象的访问,创建对象的目的就是为了使用对象,java程序通过栈上的reference数据来操作具体对象,通过reference类型去操作对象有两种定位方式:

一是:句柄方式,java虚拟机在堆中开一块空间作为句柄池,reference持有句柄的具体地址,句柄中存放着对象的实例数据地址和具体类型地址。

在这里插入图片描述
二是:直接指针访问方式,也就是reference持有对象的具体地址,可以直接访问到对象,然后通过对象中对象头信息存储的类元数据信息找到对象的类型数据。(HotSpot VM 就是通过直接指针访问方式访问的)。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_37323658/article/details/88997244