引言
推荐阅读:JVM高级特性与最佳实战(一)————JAVA内存区域
今天我们来继续讲解使用new关键字以后,在虚拟机中究竟是怎么创建对象的,以及分析对象的内存布局和访问定位。
对象的创建过程
- 类加载检查
当虚拟机遇到一条new指令时,首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载,解析和初始化过。如果没有必须先执行相应的类加载过程。 - 分配内存
**类加载检查通过后,为新生对象分配内存,**按照java堆的连续性,分配内存有两种方式:一种是基于连续的指针碰撞分配法,一种是基于不连续的空闲列表法。
那么怎么解决多个对象分配内存操作时的原子性呢?
(1)对分配空间的动作进行同步处理,JVM采用cas配上失败重试的方式保证更新操作的原子性。
(2)第二种是把每个对象分配内存的动作按照线程划分在不同的空间上进行,做法是每个线程在java堆中预先分配一小块内存,称为本地线程分配缓冲(TLAB),哪个线程需要分配内存,就在该线程TLAB上分配,只有在TLAB用完并分配新的TLAB时,才需要同步锁定。 - 赋初值
内存分配完成后,虚拟机需要将分配到的内存空间初始化为零值,如果使用了TLAB模式,那么这一动作也可以提前至TLAB分配时进行。这一操作保证了对象的实例字段在java代码中可以不赋予初值就可以直接使用。 - 设置对象属性
设置一些引用,对象的哈希码,GC分代年龄等信息。
对象的内存布局
在HotSpot虚拟机中,对象在内存中的布局可以分为三块区域:对象头(header),实例数据(IntanceData)和对齐填充(Padding)。
- 对像头
(1)MarkWord:存储对象自身的运行时数据(MarkWord),如哈希码,GC分代年龄,锁状态标志,线程锁,偏向线程ID,偏向时间戳等等
(2)类型指针:对象指向他的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。 - 实例数据
这部分是程序代码中定义的各种类型的字段内容。有两个特点:
(1)父类和子类的都要记录下来。
(2)相同宽度的字段一般会分配到一起。 - 对齐填充
当对象实例数据没有对齐时,需要对齐填充来补充。
对象的访问定位
我们的JAVA程序需要通过栈上的reference数据来操作堆上的具体对象。由于JVM没有规定具体的细节,目前虚拟机厂商主流的对象访问方式主要有两种:
(1)如果使用句柄访问,那么JAVA堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。
(2)如果使用直接指针访问,那么Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息。
图示:
句柄访问:
直接指针访问:
优势分析:
(1)使用句柄来访问的优势是refrence中存储的是稳定的句柄地址,在对象被移动时只会改变句柄池中的实例数据的指针,refrence本身不需要修改。
(2)指针直接访问速度很快,节省了一次指针定位的开销。
总结
没啥好总结的,期待大家的点赞及关注!
作者:select you from me
来源:优快云
转载请联系作者获得授权并注明出处。