Java虚拟机学习笔记第一章第二部分
Java对象的创建
1.检查参数是否已经在常量池中,
2.检查该类有无被加载,解析,初始化
3.分配内存,这时Java堆中,中间的分界用的指针向空闲区域方向移动,这种分配方式也叫做指针碰撞, 若Java堆空间内 存并不规整,那么就需要根据一个空闲列表(Free List),来进行分配内存,Java堆是否规整由垃圾收集器决定。
创建对象时涉及指针的问题,同时也会涉及同步的问题。解决方案有两种:
1.CAS失败重试(乐观锁Compare and swap)
CAS
简单来讲就是如果预期值就是实际值的话,OK,进行修改,如果不是的话,失败,返回改变后的值。详情可看下面这个。
(68条消息) CAS配上重试_QuinnNorris的博客-CSDN博客_cas配上失败重试
2.TLab(本地线程分配缓冲)
即早就分配给你一部分内存了,自己用,如果用完了,需要给TLab分配的时候,还是需要同步锁定,因此两个方案本质都是同步锁。是否使用TLab可以通过**-XX:+/-UseTLab**参数来设定。
内存分配完成后会进行初始化,为0值,但不包括对象头,对于TLab,可以提前到TLab分配时进行,以便写代码的时候可以不赋初始值就可以直接使用。
初始化之后会对对象进行配置,配置对象头,指向类的指针等。
对象头
对象头包含三个部分,Mark word,指向类的指针,数组长度(数组独有)
Markword 32位的HotSpot根据锁的状态,存储不同的数据,如图
指向类的指针:指向该类,Java的类数据存储在方法区中。 关于对象头将在对象的内存创建中说明。
虚拟机对象创建完成,之后进行INIT 方法,将对象按照程序员的要求初始化。
对象的内存创建
HotSpot虚拟机中,对象分为三块区域。对象头(Header),实例数据(Instance Data),对其补充(padding)。
对象头:包括两部分
1.运行时数据,即“Mark Word”。
哈希码:用于区分对象。
GC分代年龄:确定对象状态,对应GC回收。
锁状态:01 00 10 11 01 确定使用什么锁。
偏向线程ID: 偏向模式的时候,当某个线程持有对象的时候,对象这里就会被置为该线程的ID。
偏向时间戳(Epoch):表示线程有效性(此处不太理解)
Markword是一个非固定数据结构,根据对象状态,复用空间。
2.类型指针,指向类,说明这个方法是哪个类的。并不是所有虚拟机都有类型指针。
实例数据
实例数据储存真正有效数据,包括子类与父类,存储顺序受虚拟机分配策略参数与代码中的顺序决定。
分配策略,相同宽度的字段总是被分配到一起,oops(Ordinary Object Pointers),父类定义的变量在子类之前,如果CompactFields为True,那么子类的窄的变量可能会插入到父类之中。
对齐填充
对象必须是8字节整数倍,所以需要对齐。
对象的访问定位。
对像的实例数据存储在Java堆中,而它的类信息,方法信息等属于对象类型数据存储在方法区当中,它的reference引用则存储在栈的本地变量表中,我们一般通过reference来使用对象,那么如何通过reference定位呢,一般有两种方法。
1.通过句柄访问
一般是在Java堆中独立分成一片区域作为句柄池,句柄池细分两个指针,一个指针指向实例池的实例数据,一个指针指向方法区的类型数据。好处在于,在垃圾回收的时候,对象位置会改变,这是只改变句柄池即可,reference不需要变化。
2.直接指针访问,即reference在指向Java堆,可以直接获得对象实例数据,对象实例数据中包含到对象类型的指针。优点在于获得对象实例数据的时候减少了一次的指针定位时间开销。
Hotspot采用第二种。两种方式在开发中都比较常见。//图片引自百度