创建对象
检查
当使用new关键字创建对象时,虚拟机进行类加载检查,也就是检查相应类的符号引用是否在常量池,这个类是否被加载过(加载、解析、初始化)。
分配内存
给新生对象分配内存有两种方法:空闲列表和指针碰撞。使用哪种方法取决于堆内存是否规整,而堆内存是否规整取决于垃圾收集器是否有内存压缩整理的功能。空闲列表是记录可用的内存块,分配时选择足够大的一块。指针碰撞是用一个指针将堆内存分为已分配和空闲两个连续的部分。
多线程安全
在多线程场景下,创建对象可能有两个线程将同一块内存分配给各自的对象的情况。虚拟机为保证多线程安全会同步分配内存的操作。更进一步的策略是预先在堆上给每个线程分配一块空间(TLAB),线程在各自的空间内分配空间,只有当自己的空间用完需要申请额外的空间时才会触发同步锁定,这种策略提高了创建对象在多线程中的效率。
初始化
给对象分配的内存会被初始化为零值,这意味着对象的成员变量不经过赋值就能直接使用。然后虚拟机将这个对象属于哪个类、这个类的信息、对象的哈希码、垃圾回收分代等信息都存入对象的对象头。至此,所有对象通用的流程结束,虚拟机开始根据程序员提供的初始化方法初始化对象。
对象的内存布局
对象在内存中包括三部分:对象头、实例数据、对齐。对象头包括对象自身运行数据,如哈希码、垃圾收集分代、持有的锁等;也包括类指针,也就是对象是哪个类的实例。
对象的访问定位
Java的对象通过引用来访问堆中的对象,引用和内存中的对象的关系有两种:句柄和直接指针,它们也是对象的两种访问定位方式。
句柄
句柄的集合句柄池存在堆里。一个句柄包含两种指针:实例数据指针和类型数据指针,实例数据指针指向堆里存储的对象的实例数据,类型数据指针指向方法区中对应类的信息。引用类型指向句柄,通过句柄来访问对象。
直接指针
引用类型直接指向堆中的对象,需要访问类型数据时通过对象中类型数据指针到方法区访问。
两种方式相比,句柄的地址稳定,即使对象存储位置移动,reference指向的句柄位置保持不变;直接指针少了一层指针定位的时间,速度更快。HotSpot主要使用直接指针。