JAVA对象创建过程初识

2021-08-08

一、 对象的创建

1. 执行new 操作时,会先看能不能在常量池中定位到这个类的符号引用,然后检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有就要先执行相应的类加载过程。

2. 类加载过后虚拟机将给这个新的实例分配内存。对象(实例)所需要的内存大小在类加载完成后就可以确定了。

3. 内存分配完成后 虚拟机必须把分配的内存空间初始化,各字段设为0值。

4. 接下来,虚拟机还要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码(实际上对象的哈希码会延后到真正调用Object::hashCode方法时才计算)、对象的GC分代年龄等信息。这些信息存放在对象头中。

 

从虚拟机的角度来看,执行完上面四个操作后一个新的对象就已经产生了。但是从JAVA程序来看还不够,只能说刚刚开始。

Class文件中的init方法还要执行,所有字段都还是初识0值,对象需要的其他资源也都还没ok。

 

二、 对象的内存布局

在HotSpot虚拟机对象里,对象在内存中的存储布局可以划分为三个部分:对象头Header、实例数据Instance Data和对齐填充Padding。

2.1 对象头

对象头部分又包含两类信息,1是用于存储对象自身的运行时数据,如哈希码HashCode、GC分代年龄、锁状态标识、线程持有锁、偏向线程ID、偏向时间戳等。这部分数据的长度在32位和64位虚拟机中分别为32各bit和64bit,官方称为Mark Word.

2是类型指针,即对象指向它的类型元数据的指针,JAVA虚拟机通过这个指针来确定该对象是哪个类的实例。

 

2.2 实例数据

这里才是对象真正存储有效信息的地方,如在代码中定义的字段啊还是从父类继承下来的。

 

三、 对象的访问定位

创建对象是为了访问对象。JAVA程序通过栈上的reference数据来操作堆上的具体对象。jvms里面只规定了reference类型是一个指向对象的引用,并没有定义这个引用应该通过什么方式去定位、访问堆中对象的具体位置,所以对象访问方式是由虚拟机实现来定的,主流的方式有使用句柄和直接指针两种方式。

1. 如果使用句柄的话,堆中会划分一块内存来作为句柄池,reference就是句柄池中句柄的地址,而句柄中存储了实例池中对象实例数据的指针和方法区中对象类型数据的指针。

2. 如果使用直接指针,堆中对象的内存布局就要考虑怎么放置对象类型数据相关的信息。reference就是对象的地址,如果需要访问对象的实例数据就不需要再中转一次了。

这两种对象访问方式各有优势,使用句柄来访问最大好处就是reference是句柄池中句柄地址,在对象被移动(垃圾收集时)只用改变句柄中的实例数据地址就好了,而reference不需要修改。

使用直接指针就是更快,访问实例数据更快。