1.对象的实例化
1.1 创建对象的方式
1.2 创建对象的步骤
- 字节码的角度:
执行Object o = new Object();
-
执行步骤角度:
-
判断对象是否加载、链接、初始化,然后加载类元信息:
- 遇到new指令先去检查能否在元空间中找到符号引用,并检查该引用代表的类是否被加载、链接、初始化(即类的元信息是否存在)
- 没有则在双亲委派模式下,使用当前类加载器以
ClassLoader+包名+类名
为key找对应的class文件 - 没找到就抛出
ClassNotFoundException
异常;找到就加载类并生成相应的class类对象
-
为对象分配内存: 先计算对象占用空间大小,在堆中划分内存给该对象。如果实例成员变量是引用变量,则仅分配引用变量空间即可(4/8个字节)
- 如果堆的内存规整,JVM会采用指针碰撞法分配内存(使用过的内存放在一边,未使用过的放在另一边,中间放置指针作为分界点的指示器)
- 如果堆的内存不规整,JVM会采用空闲列表分配内存(维护一个列表记录哪些内存块可以使用)
-
处理并发问题:
- 采用CAS+失败重试保证更新的原子性
- 在Eden区为每个线程分配一块TLAB
-
初始化分配的空间: 所有属性设置默认值
-
设置对象的对象头:将对象所属类(即类的元数据信息)、对象的HashCode和对象的GC信息、锁信息等数据存储在对象头中
-
执行init方法进行初始化(注意
<cinit>
方法是对静态属性进行初始化):包括显示初始化、代码块中初始化以及构造器中初始化
-
2.对象的内存布局
创建完对象后,该对象在堆空间的内存布局如下:
执行
Customer c = new Customer();
的内存布局:public class Customer{ int id = 1001; String name; Account acct; { name = "匿名用户"; } public Customer{ acct = new Account(); } } class Account{}
3.对象的访问定位
JVM是如何通过栈帧中的对象引用访问到其内部的对象实例?
- 句柄访问:
- 需要专门开辟一个句柄池
- 访问效率低(找实例数据还需要经过两次索引)
- reference始终指向句柄池,当对象改变时不需要改变reference而是改变句柄池中的指针
- 直接指针(HotSpot默认):
- 无需开辟额外空间
- 访问效率高
- 当对象改变时需要改变reference
4.总结
1.对象在JVM如何存储?
2.对象头有什么内容?