JVM深入浅出(2)--- 对象创建/内存布局/访问方式

1. 对象创建/内存布局/访问方式

1.1 对象的创建

  1. 检查是否执行类加载流程

    • 首先当jvm执行到new指令时候,会根据参数定位常量池中的类的符号引用,通过这个符号引用,判断对应的类是否有经历加载,连接,初始化的流程,如果没有,则先执行类加载过程。
  2. 当类加载通过后,就是为新的对象分配内存,两种分配内存方式

    • 指针碰撞: java内存中的是绝对规律的,空闲内存放在一遍,分配过的内存放一遍,中间放一个指针作为分界点的指示器,这种情况就直接根据要分配的内存大小,移动指针即可。

    • 空闲列表: 当java堆的内存使用情况不规则时,就必须维护一个空闲列表,记录哪些内存是可以被分配的。

  3. 对于多线程分配内存的情况,也有对应的措施保证线程安全

    • CAS(compare and swap)--乐观锁 : CAS是不加锁的处理线程安全的方式,也就是说他是乐观锁。CAS是在线程每次对一块内存赋值时,先记录这块内存的初始值,当确实要赋值时,比较当前的值是否和初始值一致,如果一样的话,说明这块内存没有被别的线程动过,则赋值并return true。 否则则return false。
    • TLAB:这个之前提到过,TLAB是多线程中,每个线程在堆中划分一小块内存,用于对象内存分配,这样也能保证线程安全。
  4. 对象初始化:对于对象的属性做初始化赋值(如boolean - false, int - 0);

  5. 对象头设置,设置对象头中的数据,如指向方法区类的指针,hashcode,gc分代年龄。

  6. 执行<init>方法(也就是对象的构造方法)

1.2 对象的内存布局

  • 对象头:分为两类信息
    • Mark Word:主要是对象运行时的一些数据。 比如:hashcode,gc年龄,线程持有锁(锁状态)
    • 类型指针:指向方法区的类元数据,对象通过这个指针确认是哪个类的实例。
  • 实例数据
  • 对齐填充:因为要求对象起始地址必须是8字节的整数倍,也就是说对象都是8字节整数倍

1.3 对象访问定位

访问对象的两种方式

  • 直接指针:通过直接指向堆中对象访问
  • 句柄
    • 句柄也就是在堆内存中开一块句柄池,句柄指向堆中对象实例数据和方法区的对象类型数据,栈中引用指向的就是句柄池中的句柄地址

对比两种方式

  • 句柄的好处在于,栈中reference储存的是稳定的句柄地址,当对象实例改变,不影响reference的改变
  • 直接指针在于,他访问更快,一次访问就能访问到具体的对象。
posted @ 2026-04-06 12:34  不会coding的喵酱  阅读(1)  评论(0)    收藏  举报