Java对象

来源:互联网 发布:法属圭亚那算法国嘛 编辑:程序博客网 时间:2024/05/18 23:14

对象创建

类加载

首先jvm会判断这个类是否已经被加载、解析和初始化,如果没有就进行相应的类加载过程

分配空间

分配方法

通过类加载检查后,就需要在java堆中实际开辟一块空间来容纳新对象,在实际中使用的垃圾收集算法不同,所以java堆不一定是规整的,所以不同算法使用的分配方法不同:

  • 指针碰撞:
    如果垃圾收集器采用的是标记-整理算法,那么在内存中是觉得规整的,所以只需要一个记录分界点的记录器,所有用过的内存在分界点的一边,所有没有用过的内存在分界点的另一边,分配内存只需要将分界点往空闲区域移动与对象大小相等的距离
  • 空闲列表:
    如果使用标记-清除算法,那么在内存中是不规整的,所以需要额外的一个列表来记录所有可用的的块,在分配的时候从列表中找到一块足够大的空间划分给对象实例

分配空间的原子性

空间分配是一个非常频繁的行为,所以在并发条件下很难保证线程安全,jvm提供2种方式来保证原子性:
* 采用CAS的方式保证更新操作的原子性
* 把内存分配按线程划分在不同的空间中(TLAB)

执行构造方法

分配完空间操作时,会将分配到的内存空间都初始化为0值,然后需要执行构造方法来把对象按照意愿进行初始化

对象内存布局

包括三块区域:

  • 对象头(header)
  • 实例数据(instance data)
  • 对齐填充(padding)

对象头

在非数组类型中,对象头分2部分,第一部分叫mark word, 用于存储对象的运行时数据1,第二部分用于指向他的类元数据(Class)的指针,来确定是哪个类的实例,在数组类型中,还有一块记录数组长度的数据。

实例数据

存储代码中定义的各种类型的字段内容

填充区域

不是必然存在,起占位符的作用,保证对象大小是8字节的整数倍。

对象访问定位

jvm有两种方式实现对象的访问:
* 句柄访问
* 直接访问

句柄访问

在java堆中额外开辟一块内存作为句柄池,在java栈中存储的地址是句柄在句柄池中的地址,在句柄中,存放对象在java堆中的地址。
优势:在对象移动时,只需改变句柄中的地址,无需改变reference。

直接访问

直接在java栈中存放堆中地址。hotspot jvm使用这种方式。
优势:较句柄访问节省了一次定位的时间开销。速度更快。


  1. 如hashCode,GC分代年龄,锁状态标志,线程持有的锁,偏向线程的id,偏向时间戳等 ↩