对象创建
-
new 类名
-
根据new 的参数在常量池中定位一个类的符号引用
-
如果没有找到这个符号引用,说明类还没有被加载,则进行类的加载,解析和初始化。
-
虚拟机为对象分配内存(位于堆中)
-
将分配的内存初始化为零值(不包括对象头)
-
调用对象的 方法。
对象分配内存
内存分配 --》java堆是否规整 决定–》垃圾回收策略决定(是否有压缩整理功能–有规则:已使用与未使用空间)
指针碰撞(内存分配规整)
- 用过的内存放一边,没用过的内存放一边,中间用一个指针分隔;
- 分配内存的过程就是将指针向没用过的内存那边移动所需的长度;
空闲列表(内存分配不规整)
- 维护一个列表,记录哪些内存块是可用的;
- 分配内存时,从列表上选取一块足够大的空间分给对象,并更新列表上的记录;
线程安全性问题
指针碰撞,空闲列表都可能有线程问题。
-
对分配内存空间的动作进行同步处理(CAS); --加锁
CAS 即比较再交换,是一种无锁算法。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当要将v修改成B的时候,需要先将v和A进行比较,如果相等,才操作,如果不等,则不进行操作。(并发可能问题)
-
把内存分配动作按照线程划分在不同的空间之中进行;
- 每个线程在 Java 堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB);
- 哪个线程要分配内存就在哪个线程的 TLAB 上分配,TLAB 用完需要分配新的 TLAB 时,才需要同步锁定;
- 通过
-XX:+/-UseTLAB
参数设定是否使用 TLAB。
对象的结构
-
Header(对象头)
-
自身运行时的数据(Mark Word)
哈希值,GC分带年龄(垃圾回收),锁状态标志,线程持有的锁,偏向编程ID,偏向时间戳
-
类型指针
对象指向元数据的指针。
-
-
InstanceDate (实例数据)
- 是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容
- 无论是从父类继承下来的,还是在子类中定义的,都需要记录起来
-
Padding (对齐填充)
- 对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。
- 由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或者2倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。
对象的访问定位
-
使用句柄
ava堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据的具体各自的地址信息。
好处:就是reference中存储的是稳定句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要被修改。
-
直接指针
Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,reference中存储的直接就是对象地址。
引用内存直接指向对象的内存区域。 -------》 速度快,提高性能–》HotSpot使用此方式
好处:就是速度更快,它节省了一次指针定位的时间开销,由于对象访问的在Java中非常频繁,因此这类开销积小成多也是一项非常可观的执行成本。