对象的创建
虚拟机遇到一条new指令时,执行以下步骤创建对象:
- 首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已被加载。解析和初始化。
- 步骤1结果没有,则执行相应的类加载过程
- JVM为新生对象分配内存,类加载完成便可确定。
- JVM堆中内存绝对规整,用过内存放一边,空闲内存放一边,中间放着一个指针来作为分界点,移动指针来分配内存,称之为“指针碰撞”
- JVM堆中内存不规整,内存相互交错,通过维护一个记录内存的列表,找到一个足够大的内存来进行分配,称之为“空闲列表”
- 每个线程在堆中预先分配一块TLAB来进行内存分配,只有TLAB用完并分配新的TLAB时,才需要同步锁定。通过-XX:+/-UseTLAB参数来设定
- 内存分配完成后,JVM将分配到内存空间都初始化零值,可提前至TLAB中完成。这一步保证了对象的实例字段在JAVA代码中不赋初始值就可直接使用
- JVM对对象进行必要设置,对象头信息等
- 以上步骤完成之后,从JVM视角来看,一个新对象已经产生了,但从Java程序角度看,对象的创建才刚刚开始,执行<init>方法完成初始化
对象的内存布局
对象在内存中可以分为3块区域:
- 对象头(Header)
包含2部分:
第一部分用于存储对象自身运行时数据,如哈希码,GC分代年龄,所状态标志、线程持有的锁。偏向线程ID、偏向时间戳,这部分数据成为“Mark Word”;
另一部分是类型指针,指向它的类元数据的指针;
如果对象是数组,对象头中必须有一块用于记录数组的长度 - 实例数据(Instance Data),对象真正存储的有效信息
- 对齐填充(Padding),起占位符作用,通过填充来补全对齐
对象的访问定位
jvm通过虚拟机栈上的reference数据来操作堆上的具体对象,有2种访问方式
- 句柄访问:jvm堆中划分出一块内存来作为句柄,reference存储的就是对象的句柄地址,句柄中包含了对象实例数据与类型数据各种具体的地址信息;
优点:地址不会变,对象被移动也之后改变句柄中的实例数据的指针
缺点:需要额外的空间来存储,访问要经过2次指针定位 - 直接访问:reference中存储的直接就是对象地址
优点:速度快,节省了1次指针定位
网友评论