对象实例化及内存布局
2020-09-25
创建对象的步骤 #
-
当遇到一条new指令时,首先判断能否在常量池中定位到一个类的符号应用,并检查这个符号引用代表的类是否加载、解析和初始化
-
为对象分配内存,对象所需的内存大小在类加载完后就可完全确定
- 如果内存规整,指针碰撞。
- 如果内存不规整,虚拟机需要维护一个列表,空闲列表分配。
-
处理并发安全问题
- 采用cas失败重试
- 每个线程预先分配一块TLAB,通过-XX:+/-UseTLAB参数来设置。
-
对象属性初始化,即所有属性设置0值。
-
设置对象的对象头。
-
属性的显式初始化、代码块中初始化、执行init方法进行初始化。
对象的内存布局 #
-
对象头,包括如下信息:
- 运行时元数据:哈希值,gc分代年龄,锁状态标志,线程持有的锁,偏向线程id,偏向时间戳。
- 类型指针:指向类元数据的指针,确定该对象是哪个类的实例。
- 如果是数组,还需记录数组的长度。
-
实例数据:它是真正存储的有效信息,包括程序代码中定义的各种类型的字段(包括从父类继承下来的和本身拥有的字段)。
- 规则:相同宽度的字段总是被分配在一起;父类定义的变量会出现在子类之前;如果compactFields参数为true(默认为true),子类的窄变量可能插入到父类变量的空隙。
-
对齐填充
- 仅仅起着占位符的作用,hotspot虚拟机要求任何对象的大小都必须是8字节的整数倍。
访问定位 #
- 直接指针
- 句柄访问
- java堆中会划分出一块内存来作为句柄池,引用中存放的就是对象的句柄地址,句柄中包含了对象实例数据的指针和对象类型的指针。