1.2.HotSpot虚拟机对象探秘
1.2.1 对象的创建
-
虚拟机遇到一条new指令时,首先将去检查这个指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。
-
在类检查通过后,接下来虚拟机将为新生对象分配内存。方式:“指针碰撞”,“空闲列表”。选择哪一种由Java堆是否规整决定。(分配内存过程可能存在线程安全问题,解决方法有两种。①对分配内存空间的动作进行同步处理,实际上虚拟机上采用CAS配上失败重试的方式保证更新操作的原子性 ②把内存分配的动作划分在不同的空间中进行,"本地线程分配缓冲")
-
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零(不包括对象头)
-
虚拟机要对对象进行必要的设置(对象是哪个类的实例,如何才能找到类的元数据信息,对象的哈希码,对象的GC分代),这些设置信息存放在对象的对象头之中。
完成以上内存,在虚拟机的角度 0一个对象已经创建成功了,但在Java程序的角度,对象创建才刚刚开始。<init>方法还没执行。
1.2.2 对象的内存布局
在HotSpot虚拟机中,对象在内存中存储的布局可分为三块区域:对象头,实例数据和对齐填充。
1) 对象头:对象头包括两部分信息
1.“Mark Word”:用于存储运行时数据
2.类型指针,即对象指向他的类元数据的指针(不是必须保留的)
PS:如果对象是个Java数组,那在对象头中还必须有一块用于存放记录数组长度的数据
2)实例数据:对象真正储存的有效信息,也是程序代码中所定义的各种类型的字段内容。(这部分的存储顺序受到虚拟机分配策略参数和字段在Java中定义顺序的影响)相同宽度的字段总是被分配到一起,在此条件下父类中定义的变量会出现在子类前面。
3)对齐填充(非必然存在、没有特殊含义、占位符作用)
对象的大小必须是8字节的整数倍,对象实例没能对齐时就需要对齐填充。
1.2.3 对象的访问定位
Java程序需要通过栈上的reference数据来操作堆上的具体对象。
对象访问方式取决于虚拟机实现,主流两种:句柄和直接指针
-
句柄:Java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,句柄中包含了对象实例数据与类型数据各自的地址信息
-
直接指针:reference中存储的直接就是对象地址
使用句柄来访问的最大好处就是reference中存储的是稳定的句柄地址,在对象被移动时(GC等)只会改变句柄中的实例数据指针,reference本身无需改变。
使用直接指针访问的最大好处就是速度更快,节省了一次指针定位的时间开销。