Java是一门面向对象的语言,Java程序运行时不断的创建和销毁对象,本期的话题我们就来聊聊Java对象,以及它是如何被创建的?
在语言层面上创建对象通常只需要简单的使用一个new关键字即可完成:
Object object= new Object()
虚拟机在遇到一条new指令时,首先会去检查这个指令的参数是否可以在常量池中找到这个类的符号引用,并且检查这个符号引用所对应的类是否已经被加载、解析、初始化过了。如果没有则需要进行相应的类加载过程。对象的大小在类加载完成的时候就已经确定。通常Jvm有两种方式来保证并发情况下对象的创建过程是线程安全的
采用 CAS 失败以后重试的方式保证更新操作的原子性。
本地线程分配缓存(TLAB),在线程给对象分配内存之前,Jvm 已经为每一个线程分配了线程缓冲区。
内存分配完成,Jvm 需要将分配到的内存空间初始化为默认值,如果使用了 TLAB,那么这一步在分配 TLAB 时就已经完成。
接下来虚拟机需要对对象进行必要的设置,例如对象是属于哪个实例,对象的哈希码,GC分代信息,这些信息都会保存到对象头中,以及根据Jvm运行状态的差异设置,是否启用偏向锁等信息。
经过以上步骤从虚拟机的角度看,一个新的对象已经被创建了,但是从Java程序的视角看对象的创建才刚开始,方法还没有执行,通常执行完new指令之后会接着执行方法,对象进行初始化,这样一个真正可用的对象才算完全产生出来。
对象在内存中的布局可以分为三块区域
对象头 Header
对象头的数据分为两个部分,第一、存储对象自身的运行时数据,如 hashCode, GC 的分代年龄,锁状态标志,线程持有锁。这部分数据也被称为Mark Word第二、类型指针,对象指向其所属Class的指针,虚拟通过该指针确定对象的类型。
实例数据 Instance Data
对象真正存储的有效数据,程序中定义的各种数据。
对齐填充 Padding
不是必然存在,也没有特殊意义,仅仅是占位符的作用。
使用指针和直接指针两种方式
使用句柄访问对象,Java堆中会划分出一块内存来作为句柄池,本地变量表中的 reference 存储的就是对象的句柄地址。reference 存储的是稳定的句柄地址在对象被移动时只会改变句柄中到对象的数据指针,而 reference 则不需要改变。

使用直接指针访问,对象实例数据中将存储类型数据的相关信息。相比句柄访问可以减少一次指针定位的开销。

两种方式各有优劣,我们所讨论的HotSpot采用的是直接指针的方式进行对象的访问定位的。
网友评论