在此简单的记录一下《深入理解Java虚拟机》第2章的2.3节内容。
对象的创建
这里的对象的创建是指普通的对象(不包括数组和Class对象)。对象的创建简单来说就是执行new的时候,虚拟机做出对应的响应。让我们看看一下虚拟机创建对象的过程:
1.虚拟机遇到new指令时,首先尝试在常量池中定位到对应类的符号引用,并检查这个符号引用代表类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程(后续会写一下关于类加载的问题)。
2.类加载检查通过后,为新生对象分配内存。对象内存的大小在类加载完成后便可完全确定。对象内存分配有“指针碰撞”和“空闲列表”两种方法,“指针碰撞”是把已用内存放到指针的一边,未用的放到另一边,以指针分隔,当需要分配一个新对象内存时把指针往未分配内存那边移动相对应的空间即可;“空闲列表”是因为内存已用的和未用的并不是规整的,它们是交错的,所以需要一个列表记录内存块的情况。Java堆是线程之间共享的内存,虚拟机采用CAS配上失败重试的方式保证更新操作的原子性保证内存指针修改并发安全性;另一种方法是“本地线程分配缓冲(Thread Local Allocation Buffer TLAB)”。
3.将虚拟机分配到的内存空间初始化为零值。
4.对对象进行必要的设置。其实是对对象头编写。
5.完成上面4个步骤执行new指令后会接着执行
对象的内存布局
对象在内存中存储的布局可分为3部分:对像头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
- 对象头
对象头包括两部分信息:第一部分用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等;另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。如果对象是一个Java数组,那么对象头还必须有一块用于记录数组长度的数据。因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组大小。 - 实例数据
实例数据部分是对象真正存储的有效信息。 - 对象填充
因为自动内存管理系统要求对象的大小必须是8字节的整数倍,才有对象填充的说法。
对象的访问定位
Java程序需要通过栈上的reference数据来操作堆上的具体对象。reference类型存放的是对象的引用,但是具体怎么样访问对象取决于虚拟机实现而定。目前主流的访问方式有“使用句柄”和“直接指针”两种。