1. 对象创建
在语言层次上创建一个对象,只是一个简单的new关键字,但在虚拟机中对象的创建有如下的过程
-
先检查能不能在常量池中定位到符号引用,并检查这个类是否已经被加载,解析,初始化过了,如果没有,那么必须先执行相应的类加载过程
-
类加载检查通过后,进行内存分配,分为指针碰撞和空闲列表,同时对象的创建在虚拟机中是非常频繁的行为,在并发的情况下不一定是线程安全的,比如给A分配了内存,指针还没修改,B就使用了原来的指针分配内存,通常有两种解决方案,第一种是对分配空间的动作做同步处理,另一种是把内存分配的动作按照线程划分在不同的空间中,即每个Java堆中分配一小块内存作为本地线程分配缓冲 TLAB(thread local allocation buffer)。哪个线程要分配内存就在哪个TLAB上面分配,只有当TLAB用完分配新的时候才需要做锁定,虚拟机是否开启 TLAB可以做设置
-
内存分配完成后,要将分配到的内存空间都初始化为0
-
设置对象头,入对象的hash码,对象的GC分代年龄信息*
*执行 方法,按照程序员的意愿进行初始化,也就是构造方法?
2. 对象的内存布局
在HotSpot虚拟机中,对象在内存的存储布局可以分为三块区域: 对象头,实例数据,对齐填充
-
对象头可以分为两部分,第一个部分是Mark Word,被设计成一个非固定的数据结构,而且会根据状态的信息复用自己的存储空间
另一个部分是类型指针,即是对象指向它的类元数据的指针,虚拟机通过这个指针类确定这个对象是哪个类的实例,但有的虚拟机不是这样实现的,如果对象是一个Java数组,那么还要一块用于记录数组长度的数据,因为虚拟机可以判断Java对象大小,但是无法判断数据大小
-
实例数据 存储顺序:受虚拟机分配策略参数(FieldAllocationStyle)和字段定义顺序影响,Hotspot VM的默认顺序为:longs/doubles, ints,shorts/chars,bytes/booleans,oops(Ordinary ObjectPointers),即相同⻓度字段被分配在⼀起,除此之外,⽗类成员总 是在⼦类成员之前。若CompactFields参数为true,则⼦类中⻓度 较⼩的字段也可能被插⼊到⽗类字段的间隙中。
-
对齐填充不一定存在,只是起占位填充的作⽤。Hotspot VM中的⾃动内存管理系统要求对象的起始地址必须是8的整数倍,也就是对象的⼤⼩必须是8 的整数倍,⽽对象头部分正好是8字节的倍数,所以实例数据部分不对⻬时 需要通过对⻬填充来补全。
3. 对象的访问定位
建立对象是为了使用对象,我们Java程序需要通过栈上的reference数据来操作堆上的具体对象,目前主流的访问方式有两种,句柄和直接指针
- 在Java堆中分配出一块内存作为句柄池,reference存储的就是对象的句柄地址,句柄中包含了对象的实例数据,和类型数据各自的具体信息地址

- 如果使用直接指针访问,那么在Java堆的对象中布局就要必须考虑如何放置类型数据的相关信息,在reference中保存的就是直接对象地址
两种方法各有千秋,句柄的话,我们移动对象的话,只要修改句柄中的实例数据指针不需要修改reference,指针访问的话就是速度更快,Hotspot VM采⽤直接指针进⾏对象访问定位。