JVM---对象
/** * 【对象的实例化】 * <创建对象的方式> * 1、new * a, new XXX * b, XXX.static方法 * c, XXXBuilder/ XXXFactory的方法 * 2、反射 * a, Class的newInstance * ***空参构造器,权限必须是public * b, Constructor的newInstance * ***空参(或带参)构造器,权限不限制 * 3、clone * ***不调用构造器 * 当前类需要实现Cloneable接口,实现clone() * 4、反序列化 * 从网络、文件中获取 一个对象的二进制字节流 * 5、三方库Objenesis * 动态生成对象 * * <创建对象的步骤> * * Object object = new Object(); * * 0: new #2 // class java/lang/Object * 3: dup * 4: invokespecial #1 // Method java/lang/Object."<init>":()V 对象成员变量显式初始化 * 7: astore_1 * 8: return * * * 1,判断对象对应的类是否加载、链接、初始化 * 当 JVM遇到new指令,首先去检查new指令的参数 能否在方法区的常量池中找到该类的引用,并检查该类是否被加载、链接、初始化: * 如果没有,在双亲委派的模式下,使用当前类加载器查找对应的class文件: * 若没有找到文件,抛出ClassNotFoundException; * 找到文件,进行类加载; * * 2,计算 对象所占堆内存大小,然后在堆中分配内存给新对象; * 如果 对象成员变量 是引用类型,只需要分配引用变量空间即可,4字节; * byte,short,int,char,boolean,float,引用类型 都是 4个字节; * double,long是8个字节; * * 处理 内存分配 并发安全问题 * 1、使用CAS,保证操作的原子性 * 2、每个线程 预先 分配 一个 TLAB; * * 3,给 对象的实例属性 默认初始化; * * 4,设置 对象头 * 将对象的所属类、对象的hashcode、对象的GC信息、锁信息等 存储到对象头; * * 5,执行构造器<init>方法 进行 对象成员变量显式初始化(顺序执行): * aa,成员变量 显式 初始化; * bb,执行 实例代码块; * cc,执行 类的构造器; * dd,将 对象的地址 赋值给 引用变量 * * * <给对象属性赋值的操作> * 1、属性的默认初始化 * 2、属性的显式初始化 * 属性显式赋值、代码块显式赋值、构造器显式赋值... * * * <类的构造器<init>方法> * public class User { * * private String name = "jack"; * private int age = 10; * * { * name = "rose"; * } * * public User(){ * this.age = 11; * } * * public User(String name){ * this.name = name; * } * * public User(int age){ * this.age = age; * } * } * * 空参构造器<init> * 0 aload_0 * 1 invokespecial #1 <java/lang/Object.<init>> * 4 aload_0 * 5 ldc #2 <jack> * 7 putfield #3 <com/an/object/User.name> * 10 aload_0 * 11 bipush 10 * 13 putfield #4 <com/an/object/User.age> * 16 aload_0 * 17 ldc #5 <rose> * 19 putfield #3 <com/an/object/User.name> * 22 aload_0 * 23 bipush 11 * 25 putfield #4 <com/an/object/User.age> * 28 return * * String参数构造器<init> * 0 aload_0 * 1 invokespecial #1 <java/lang/Object.<init>> * 4 aload_0 * 5 ldc #2 <jack> * 7 putfield #3 <com/an/object/User.name> * 10 aload_0 * 11 bipush 10 * 13 putfield #4 <com/an/object/User.age> * 16 aload_0 * 17 ldc #5 <rose> * 19 putfield #3 <com/an/object/User.name> * 22 aload_0 * 23 aload_1 * 24 putfield #3 <com/an/object/User.name> * 27 return * * int参数构造器<init> * 0 aload_0 * 1 invokespecial #1 <java/lang/Object.<init>> * 4 aload_0 * 5 ldc #2 <jack> * 7 putfield #3 <com/an/object/User.name> * 10 aload_0 * 11 bipush 10 * 13 putfield #4 <com/an/object/User.age> * 16 aload_0 * 17 ldc #5 <rose> * 19 putfield #3 <com/an/object/User.name> * 22 aload_0 * 23 iload_1 * 24 putfield #4 <com/an/object/User.age> * 27 return */
/** * 【对象的内存布局】 * <对象头(Object Header)> * a,MarkWord(运行时元数据): * hashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳 * b,类型指针 * 指向 方法区中该对象所属的类型; * * ***如果是数组,还包括 数组长度; * * <实例数据(Instance Data)> * 对象真正存储的有效信息 * 包括 程序自己定义的属性、从父类继承的属性 * ***规则: * 对象中先放 父类属性,再放 子类属性; * * <对齐填充(padding)> * 非必须,起到占位符的作用; * * eg: * public class Account {} * * public class Customer { * * int id = 1001; * String name; * Account acc; * * { * name = "匿名客户"; * } * * public Customer(){ * acc = new Account(); * } * } * * * Customer cust = new Customer(); */
/** * 【对象访问】 * JVM 如何 通过栈桢中的对象引用 访问到 对象实例的? * 通过栈桢中 局部变量存储的引用类型的对象实例的具体内存地址; * * 访问方式: * 直接指针(Hotspot采用)、句柄访问 */
直接访问
句柄访问