1.字节码的角度看Java对象的创建过程
字节码如下
0 new #7 <org/example/jvmlearning/learning01/OOO>
3 dup
4 invokespecial #9 <org/example/jvmlearning/learning01/OOO. : ()V>
7 astore_1
在以上字节码可以看出,Java对象在创建的过程中,可以分成四部依次是创建初始的类对象、复制这个对象的引用指针、调用对象的init方法进行属性的初始化、返回复制好的引用地址。这个里面表示在创建一个对象的时候可能发生指令重排的现象。因此、在多线程场景下要尽可能注意一些问题。
2.类的创建角度看Java对象的创建过程
- 判断这个类是否已经被加载,即加载、链接、初始化这三个过程。如果没有则进行类的加载,如果已经完成了加载则需要获得这个类的class对象
- 分配内存空间。按照存储空间是否规整,当存储空间规整的时候,进行指针碰撞的方式进行分配,若存储空间不规整则按照空间的空闲列表挑选一个合适的空间进行分配。
- 处理并发问题(CAS,TLAB),因为要避免多个线程同时进行对象的new操作。
- 完成对象的创建以及初始化。在这个阶段,主要是完成了对象的创建操作,并且完成了对象属性的默认初始化
- 设置对象头,构成一个完整的对象
- 正式完成对象属性的初始化操作,例如依次完成属性的赋值,代码块中属性的赋值,构造方法的执行。
3.Java对象的访问方式
- 通过栈帧中的局部变量直接指向对象所存在的堆中的位置
- 通过句柄池的方式进行访问,这个访问方式的具体说明如下,在本地方法栈的局部变量表中的变量,是指向堆中的一个句柄池,这个句柄才指向了一个堆中的方法。这样做的好处就是,当对象频繁修改的时候,这个局部变量表中的变量不会频繁变化。
4.Java对象的内存布局
- 对象头,对象头由两部分组成。第一部分称为markword部分,这一部分主要是由对象的hashcode、对象的年龄、以及锁的状态等信息构成。第二部分称为kclass部分,这一部分主要是存储了对象的类信息。
- 对象的实例数据,这一部分主要是存储对象的属性信息等和对象相关的数据,因为在进行类加载的时候是先加载父类,再加载子类。因此,父类对象更靠前。
- 对齐填充,这部分没啥实际用途,主要是想让一个对象占据的空间固定,因此当一个对象的实际空间较小的时候,会填充一些无用的信息。