1、对象的创建
当jvm遇到new指令时,先检查这个指令的参数能否定位到常量池中的一个类的符号引用,如果可以定位则
1.1、为对象分配内存,一个类所占的内存大小在类加载完毕后就已经确定了.分配内存有2中方式
a、如果内存时规整的,即用过的在一边,没有用过的在一边,那么采用指针碰撞(Bumpthe Pointer)的方式管 理,即通过一个指针为界限,指针的一边为用过的另一边为没有用过的
b、如果内存不是规整的,则使用空闲列表(FreeList)来管理,即虚拟机维护一个列表,这个列表记录那些内 存时可用的具体采用哪种内存管理方式取决于垃圾回收机制是否带有压缩整理机制
1.2、由于对象的创建是非常频繁的,哪怕只是修改一个指针,在并发的环境下也有线程安全问题,即线程A分 配了内存还没有来得及修改指针,线程B有使用原来的指针分配了内存的这种情况
一般的解决方案有2种,
1、同步分配内存的操作实际是使用CAS
2、为本地线程分配缓冲,Thread Local Allocation Buffer,TLAB各个线程有自己的缓冲区,除非本地缓
冲使用完才需要同步
1.3、内存分配完后,需要对内存空间进行初始化(不包括对象头),把各个字段均赋为零值
2、对象的内存布局
对象分为3个部分即对象头(Header)、实体数据(Instance Data)、对齐填充(Padding)
2.1、对象头包括两部分信息
2.1.1、Mark Word:第一部分用于存放对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程
持有的锁、偏向线程ID、偏向时间戳等,可以通过开启指针压缩来节省空间。
2.1.2、类型指针,指向其元数据的指针即这个对象是哪个类的实例(Class),如果是数组,则必须在数组的对 象头上添加数组的长度,因为jvm可以通过普通对象可以获取java对象的大小,但是不能通过数组对象获
取其长度
2.2、实体数据(Instance Data):这部分信息是真正存放数据的部分,即代码中定义的字段的内容包括从父类继承 下来的字段等
2.3、第3部分是对齐填充部分(padding):没有实际的含义,由于hotSpot虚拟机规定对象的起始地址必须是8的倍数 即对象的大小必须是8的倍数、所以如果此对象大小不是8的倍数则补充为8的倍数
3、对象的访问定位
虚拟机中定义了一个栈上一个引用类型指向一个对象,并未定义以何种方式定位到这个对象上,所以主流的方 式有2种,即使用句柄访问和直接访问
3.1、使用句柄访问(图1)
java对中开辟一块空间用来作为句柄池,句柄中存放了对象的实例数据和类型数据
优势:栈的引用很稳定不用变、当对象发生变化时栈中数据都是句柄,只改变句柄中的数据
劣势:访问对象时多了一次寻址访问
3.2、直接访问对象(图2)
java栈上的引用直接指向堆中的对象
优势:速度快
劣势:对象频繁改变时,需要频繁改变引用的指针