JVM:HotSpot对象探秘-创建、内存分配、访问定位

今天来研究一下HotSpot中对象的创建以及内存分配、访问定位。

如何创建对象?

在java语言层面,通过new关键字创建对象,
现在要探究的是虚拟机内创建对象的过程:
1、当遇到new指令时,先根据这个指令的参数去常量池中查找是否有对应一个类的符号引用。
2、检查这个符号引用所代表的类是否加载初始化过,如果没有,则执行加载初始化的过程。
3、类加载检查通过后,虚拟机将为新生对象分配内存空间。
– 3.1 注意,在类加载完成后,对象的大小就已经可以确定下来,为新生对象分配内存空间,就是从内存中分配指定大小的空间。
为对象分配内存的方式有两种
– 3.2.1 指针碰撞
适用场景:内存规整,已分配的在一边,空闲的在另一边,中间有一个指示分界点的指针。
分配原理:把指针往空闲方向移动对象大小的距离
– 3.2.2 空闲列表
适用场景:内存不规整,已分配和空闲的内存空间交错,维护一个空闲内存的列表
分配原理:在空闲内存的列表里找到一个足够大的内存空间分配给对象,并更新列表的记录。

注意:具体使用哪一种方式给对象分配内存,取决于垃圾回收的算法是否带有压缩整理的功能。

4、在为对象完成内存分配后,会初始化对象的实例数据都为零值。
5、设置对象的对象头,包括锁标志、GC分代年龄、hashCode、属于哪个类的实例,如何才能找到元数据信息
6、new指令后执行<init>方法,按照代码进行初始化,完成一个对象的创建

对象的创建在虚拟机内是非常频繁的行为,需要考虑他是否线程安全?虚拟机是怎么解决的?

1、是否安全
理论上不是线程安全的。因为在给A对象分配内存的时候,可以指针还没有移好,B对象也参与到内存分配,这时候就会乱套
2、怎么解决?
2.1 、 CAS+失败重试保证指针更新操作的原子性[虚拟机默认采用]
2.2、TLAB :为每个线程事先分配一些内存空间,这个线程里对象的内存分配都在这一段空间内操作。当这个线程内的空间用完了,才需要通过CAS去分配新的空间。[在分配TLAB时就会初始化零值]

对象的内存布局

对象包含 对象头、实例数据、对齐填充
1、对象头,锁标志、GC分代年龄、hashCode、锁偏向ID、线程持有的锁、类型指针
2、实例数据:存储的具体数据
3、对齐填充:对象大小要求8字节的倍数,没有对齐就补全

如何实现对象的访问定位?

在java中,我们可以通过栈中的引用来找到分配在堆中的对象,这里来看下,对象的访问定位如何实现?
有两种 句柄和直接指针
1、句柄:
在java堆中会划分出一块内存作为句柄池,栈中的引用就是对象的句柄的引用,句柄持有对象实例数据地址和对象类信息的地址。

优点:GC过程中移动对象,只需要改变句柄中的对象实例数据地址,而不需要改栈中的引用。
缺点:两次指针定位,积少成多,有一定的开销成本

2、直接指针:
栈中的引用直接指向堆中对象的地址。这种访问定位方式要求对象头中包含对象的类型指针。

优点:一次指针定位即可
缺点:GC过程中移动对象,需要修改栈中的引用。

HotSpot使用第二种方式

猜你喜欢

转载自blog.csdn.net/qq_28605513/article/details/85063451
今日推荐