这里不仅仅是说“对象的创建”,而是站在JVM的角度来看对象的创建过程、内存分配和访问定位,使用对象的创建是为了帮助大家索引进来
1. JVM中对象的创建过程
在语言层面上创建对象通常仅仅是一个new关键字,而在虚拟机中,对象(普通对象,不包括数组和Class对象)的创建时怎样的过程呢?
当JVM遇到一个new指令时,
首先,检查这个new指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化。如果没有,则会先进行类加载过程。
在类加载通过之后,JVM将为新生对象分配内存。对象所需内存大小在类加载完之后便可以完全确定,所以为对象分配空间就是将一块确定大小的内存从Java堆中划分出来。分配方式通常有两种:
指针碰撞(Bump the Pointer)
Java堆中内存是规整的,被占用的内存集中在一边,并且用一个指针标识分界点。此时,分配内存就是将分界点指针向空闲方向移动此对象的大小的距离空闲列表(Free List)
Java堆中内存并不规整,此时JVM需要维护一个列表用于记录那些内存块可用,分配时从列表中找出合适的一块空间划分给对象
所以,使用哪种分配方式由Java堆是否规整决定,而堆是否规整又由所采用的GC是否带有压缩整理功能决定。因此:
- 指针碰撞采用者是带compact过程的收集器:Serial、ParNew等
- 空闲列表采用者是Mark-Sweep算法收集器:CMS(Concurrent Mark Sweep)
还有一个问题是:分配内存操作在并发情况下的线程安全问题,有两种解决方案:
- 对分配内存动作进行同步处理
实际上JVM采用CAS配合上失败重试的方式保证更新操作的原子性 - 把内存分配按照线程划分在不同的空间之中进行
即为每个线程在Java堆中预先分配一小块内存,称为:本地线程分配缓冲(Thread Local Allocation Buffer, TLAB)。这样,先在TLAB上分配,当TLAB用完时分配新的TLAB此时同步锁定。参数-XX:+/-UseTLAB
JVM将对象分配的内存空间初始化为零值,如果使用TLAB,TLAB分配时进行这一部分。
设置对象头(Object Header),例如这个对象是哪个类的实例、如果找类元数据信息、对象哈希码、对象GC分代年龄等。
以上(上面就是new指令的全部内容),在JVM的角度看一个新对象已经产生了,但是从Java程序视角来看,对象创建才刚刚开始,因为<init>方法还没有执行,所有字段还都为零。
所以,执行new指令之后会接着执行<init>方法,把对象按照程序员的意愿进行初始化,这样,一个真正可用的对象才算完全产生。
2. 对象的内存布局
在HotSpot中,对象的内存布局可以分为3块区域:对象头(Object Header)、实例数据(Instance Data)以及对齐填充(Padding)。
对象头(Object Header),对象头包括两部分:
第一部分官方称为:Mark Word (对象的运行时数据)
- 哈希码
- GC分代年龄
- 锁状态标志
- 线程持有的锁
- 偏向线程ID
- 偏向时间戳
第二部分存储是类型指针
对象指向它的类元信息的指针,通过这个指针来确定对象是哪个类的实例。
这部分并不一定需要,参考3. 对象的访问定位如果对象是Java数组,那么还得有第三部分用于记录数组长度
实例数据(Instance Data)
程序中定义的各种类型的字段内容,包括父类中定义的。对齐填充(Padding)
并不一定存在,HotSpot要求对象起始地址必须是8字节的整数倍,也就是说对象大小必须是8字节整数倍,对象头部分是符合的,所以如果实例数据部分没有对齐,就需要这部分来填充对齐。
3. 对象的访问定位
以上已经创建了对象,那么如果定位对象呢?
Java程序需要通过栈上的reference数据来操作堆上的具体对象,而JVM规范只规定了reference是一个指向对象的引用,没有规定reference如果定位和访问堆中对象的具体位置,所以对象的访问定位取决于具体的JVM实现,有两种主流方式:句柄和直接指针
句柄
使用句柄方式的话,reference存储的是对象的句柄地址。此时会将JavaHeap划分为两部分:句柄池和实例池,而一个句柄包含两个指针:实例指针、类型指针直接指针
reference存储的直接是对象地址,但对象实例数据中自身就必须包含类型指针这部分数据了
可以想到,句柄的优势是灵活性好,reference中是不变的句柄地址,而对象位置改变只需要改变句柄池中实例指针地址就可以;而直接指针的好处是速度更快,节省了一次实例指针定位开销
就HotSpot而言它采用了直接指针,但这并不是说句柄不行,主要看具体实现而已。
参考文献:
[ 1 ] 周志明.深入理解Java虚拟机[M].第2版.北京:机械工业出版社,2015.8.
[ 2 ] Tim Lindholm,Frank Yellin,Gilad Bracha,Alex Buckley.The Java® Virtual Machine Specification . Java SE 8 Edition . 英文版[EB/OL].2015-02-13.
[ 3 ] James Gosling,Bill Joy,Guy Steele,Gilad Bracha,Alex Buckley.The Java® Language Specification . Java SE 8 Edition . 英文版[EB/OL].2015-02-13.