对象创建过程
1 对象的创建
1、类加载:虚拟机遇到一条new指令时,首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查中个符号引用代表的类是否已经被加载、解析和初始化,若没有,则先执行相应的类加载过程。
2、内存分配:所需内存大小在类加载通过后便可以完全确定
指针碰撞:Java堆规整下,将指针挪动对象大小距离(标记-整理:Serial、ParNew)
空闲列表:Java堆不规整下,记录哪里有可用连续内存足够放下此对象,并更新表(标记-清除:CMS)
本地线程分配缓冲TLAB:每个线程在Java堆中预先分配一小块内存。
3、初始化:虚拟机需要将分配到的内存空间都初始化为零值(如int值为0,boolean值为false等等)。
4、更新对象头:包括该对象是谁的实例、如何找到类的元数据信息、Hash码、GC分代年龄等
5、init():程序员赋值
2 对象内存布局
HotSpot中:对象头(运行时数据+类型指针)、实例数据和对齐填充
自身运行时数据:哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳
类型指针:指向该对象元数据的指针,通过这个可以确定该对象是哪个类的实例(但查找对应的元数据不一定要靠对象)
对象Body:父类继承的字段+子类自己定义的字段,存储顺序会受到虚拟机分配策略参数(FieldsAllocationStyle)和字段在 Java 源码中定义顺序的影响。相同宽度的字段可能被分配到一起,父类变量可能出现在子类之前。
对齐填充:一般为8字节的整数倍
3 对象的访问定位
通过栈上的引用可以定位到对象位置,但无法定义到具体字段。
对象类型数据:Class信息
对象实例数据:new出来的对象
句柄访问方式:栈上引用指向句柄池地址,实现了解耦
堆中开辟句柄池,栈上引用指向句柄池。句柄中包含到对象实例数据和到对象类型数据的地址信息
优点:ref存放的是稳定的句柄地址
直接指针方式:reference直接指向对象地址
好处:节省了一次指针指向开销,速度快