JVM学习-对象的实例化、内存布局与访问定位

一、对象的实例化

1、创建对象的方法

  • new  -- 最常用的方法
  • Class的newinstance()   -- 反射的方式,只能调用空参的构造器,权限必须是public。jdk1.8之前有效
  • Constructor的newInstance()  -- 反射的方法,可以调用空参、带参的构造器,权限没有要求。jdk1.8之后有效
  • 使用clone()   -- 当前类实现Cloneable接口
  • 使用反序列化
  • 第三方库Objenesis

2、对象创建的步骤

1、判断对象对应的类是否加载、链接、初始化

虚拟机遇到一条 new 指令的时候,首先去方法去中查找这个类是否被加载过。如果没有,在双亲委派机制模式下,使用当前类加载器 ClassLoader + 包名 + 类名 作为 key 查找对应的 .class 文件,没有找到抛出 ClassNotFoundException 异常,找到的话则进行类加载,生成对应的 Class 类对象

2、为对象分配内存

计算对象占用空间大小,接着在堆中划分一块内存给新对象

3、处理分配对象内存时的并发问题

堆是线程共享的,所以在多线程情况下分配对象可能会有并发问题。一般会优先分配在每个线程的 TLAB 中,分配失败的话,会采用 CAS 失败重试、加锁来保证分配的原子性

4、为对象属性默认初始化 

也就是给对象属性赋零值

5、设置对象的对象头

将对象的所属类(类的元数据信息)、对象的 HashCode 、GC信息、锁信息等数据存储在对象头中。也就是将对象指向方法区中的类信息,将两者关联起来

6、执行 init 方法进行初始化

也就是显示初始化。如果站在程序的角度来看的话,这一步才算初始化的正式开始,在这一步会初始化成员变量,执行实例化代码块,调用类的构造方法等来初始化对象

二、对象的内存布局

大家都知道,如果 new 一个对象,就是在堆空间中开辟了一块内存,但是这块内存的布局是什么样的呢?主要包括 3 部分

  • 对象头
  • 示例数据
  • 对齐填充

1、对象头

主要包含两部分

(1)运行时元数据

  • 哈希值(HashCode):对象的地址值
  • GC分代的年龄:也就是对象的年龄,在 GC 的时候会使用
  • 锁状态标志
  • 线程持有的锁
  • 偏向线程 ID
  • 偏向时间戳

2)类型指针

指向方法区中这个对象所属的类型。也就是说每个对象都知道它的类是谁,就是由这个类型指针确定的。

注:如果创建的是数组(数组也是一个对象,也放在方法区中),还需要记录数组的长度。

2、示例数据

主要就是代码中定义的各种类型的字段(不光是自己类中的,还包括从父类继承下来的)

3、对齐填充

不是必须的,没什么特别含义,仅仅起到占位符的作用

4、画图分析

先定义如下代码:

public class Customer {
    int id = 1;
    String name;
    Account acc;
    {
        name = "张三";
    }
    public Customer() {
        acc = new Account();
    }
}

class Account {

}

/**
 * 主方法
 */
public class NewTest {
    public static void main(String[] args) {
        Customer cust = new Customer();
    }
}

在如上代码中对应的内存图如下:

  

  1. 主线程中 main() 方法入栈。main() 方法栈帧的局部变量表中有两个变量,一个是方法参数 args,一个是自己创建的Customer类的对象 cust。
  2. 我们在 new Customer() 后,会在堆空间创建一个 Customer 实例。局部变量表中 cust 指向堆空间中创建的对象实例。
  3. 在 Customer类的对象实例中,有对象的信息。包括对象头信息、实例数据信息。在实例数据信息中,有父类的实例数据,还有自己定义的实例数据,其中 id 值为 1,name 的值指向堆空间中的字符串常量中的 "匿名客户",acct 指向 Account 类的实例。
  4. Customer 对象实例中的类型指向方法区中 Customer 类的 Class 信息。Account 对象实例中的类型指向方法区中 Account类的 Class 信息。

通过以上关系图,就很清晰的知道了栈、堆、方法区之间的联系了

三、对象的访问定位

HotSpot 采用的就是指针的方法。栈中对象引用直接指向堆空间中的对象实例,堆空间中对象实例中的类型指针又指向方法区中该类的类元信息。具体如下如所示:

                         

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值