对象创建
![](https://img.haomeiwen.com/i1147652/0e063a96181d33ce.jpg)
类加载
首先jvm会判断这个类是否已经被加载、解析和初始化,如果没有就进行相应的类加载过程
分配空间
分配方法
通过类加载检查后,就需要在java堆中实际开辟一块空间来容纳新对象,在实际中使用的垃圾收集算法不同,所以java堆不一定是规整的,所以不同算法使用的分配方法不同:
- 指针碰撞:
如果垃圾收集器采用的是标记-整理算法,那么在内存中是觉得规整的,所以只需要一个记录分界点的记录器,所有用过的内存在分界点的一边,所有没有用过的内存在分界点的另一边,分配内存只需要将分界点往空闲区域移动与对象大小相等的距离 - 空闲列表:
如果使用标记-清除算法,那么在内存中是不规整的,所以需要额外的一个列表来记录所有可用的的块,在分配的时候从列表中找到一块足够大的空间划分给对象实例
分配空间的原子性
空间分配是一个非常频繁的行为,所以在并发条件下很难保证线程安全,jvm提供2种方式来保证原子性:
- 采用CAS的方式保证更新操作的原子性
- 把内存分配按线程划分在不同的空间中(TLAB)
执行构造方法
分配完空间操作时,会将分配到的内存空间都初始化为0值,然后需要执行构造方法来把对象按照意愿进行初始化
对象内存布局
包括三块区域:
- 对象头(header)
- 实例数据(instance data)
-
对齐填充(padding)
image
对象头
在非数组类型中,对象头分2部分,第一部分叫mark word
, 用于存储对象的运行时数据[1],第二部分用于指向他的类元数据(Class)的指针,来确定是哪个类的实例,在数组类型中,还有一块记录数组长度的数据。
实例数据
存储代码中定义的各种类型的字段内容
填充区域
不是必然存在,起占位符的作用,保证对象大小是8字节的整数倍。
对象访问定位
jvm有两种方式实现对象的访问:
- 句柄访问
- 直接访问
句柄访问
在java堆中额外开辟一块内存作为句柄池,在java栈中存储的地址是句柄在句柄池中的地址,在句柄中,存放对象在java堆中的地址。
优势:在对象移动时,只需改变句柄中的地址,无需改变reference。
![](https://img.haomeiwen.com/i1147652/2c34109ae36e7894.jpg)
直接访问
直接在java栈中存放堆中地址。hotspot jvm使用这种方式。
优势:较句柄访问节省了一次定位的时间开销。速度更快。
![](https://img.haomeiwen.com/i1147652/80fdbcb7fcfdf0e8.jpg)
-
如hashCode,GC分代年龄,锁状态标志,线程持有的锁,偏向线程的id,偏向时间戳等 ↩
网友评论