三、对象的创建
3.0 创建对象的方式
- new
- 反射
- 通过类(类型)—任何数据类型包括(基本数据类型)都有一个静态的属性.class --ClassLoader
- 通过字符串(类全名 )—能够实现解耦:Class.forName(str)
- 通过对象—对象.getClass()来获取c(一个Class对象)-- Object的方法
- 克隆
- 序列化
3.1 创建过程:
注意:对象的创建之前需要先加载类,
3.2 给对象分配内存
分配内存其实是指针移动的过程;
方式:
- 指针碰撞
- 空间列表
3.2.1 分配方式:
- 指针碰撞
规整的内存,向空闲区方向移动指针,执行临界区
- 空闲表
不规整的内存,维护空闲表记录空闲内存,占用内存时更新表
java堆是否规整是由垃圾回收器决定的:
- 如果垃圾回收器带有回收整理压缩的功能,就会把堆内存整理压缩成规整的区域,
- 不带回收整理功能,堆内存就不规整
3.2.3 线程安全问题
并发情况,同一时刻会有多个对象在创建,指针移动的过程可能会存在线程安全问题,或者更新空间表的过程
如何解决:
- 线程同步,加锁(效率低) CAS
- 栈上分配,本地线程分配缓冲 (效率高) – 给每个线程分配一块独立buffer内存区域(这个容量不是很大,可以通过虚拟机运行参数进行指定)
- TLAB thread local allocation burffer
3.2.4 初始化对象
初始化默认值,对象是哪个类的实例,对象的hash码,初始在对象头中
3.2.5 执行构造方法
3.3 对象的结构
3.3.1 Header(对象头)
- 自身运行时候的数据 (MarkWord )–不同平台不同, 可能是32字节或者64字节,但是一定是8字节的整数倍
- 哈希值
- 源于Object类的native int hashcode
- GC分代年龄(根据不同的代选用不同的垃圾回收算法),
- 锁状态标志(监视器中的锁状态)
- 线程持有的锁
- 偏向线程Id
- 偏向时间戳
- 哈希值
- 类型指针(确定对象是哪个类的实例,指向类的元信息)
- Class clazz = ClassA.class
- 给clazz分配了指向这块区域的指针叫类型指针
- 数组长度 只有对象数组才有
3.3.2 InstanceData(数据实例)
真正存储对象的有效信息,
相同类型的数据被分配到一起
3.3.3 Padding
相当于占位符,起填充的作用;
8字节的整数倍;
问题:在高并发场景下,如何改变锁状态,由无锁态变成轻量级锁??
3.4 对象的访问定位
3.4.1 方式
- 使用句柄 (两步:引用类型指向句柄池,句柄池指向真正的内存地址)
- 优势:栈内存中存放的引用地址始终指向句柄池不需要修改,不管对象的新增和销毁
- 缺点:两次访问,慢
- 句柄池就是一块内存空间,句柄代表一个指针
- 直接指针 (引用类型直接指向对象的内存区域) --Hotspot采用此方式
- 优势:减少一次查找,快
3.4.2 访问定位需要通过两个指针
- 到对象实例数据的指针
- 到对象类型数据的指针
不管是使用直接指针还是使用句柄都需要保存上述两个指针才可以定位到对象
参数格式
-Xms20M starting 堆的其实大小
-Xmx max 堆的最大
-Xmn new 堆的新生代大小