对象创建过程
在语言层面,对象创建通常仅仅是一个关键字new。
在虚拟机中,当遇到new时,便会进行一系列操作。
1.首先检查这个指令的参数是否能在运行常量池中找到一个类的符号引用。
若常量池中没有该类的符号引用,则说明该类没有被定义,抛出异常。
2检查这个符号代表的类是否已被加载、解析、初始化过。
若没有,则先执行相应的类加载过程。
3.虚拟机为新生对象分配内存。(对象所需内存大小在类被加载完成后便可完全确定)
指针碰撞(Serial、ParNew等带Compact过程的收集器)
假设Java内存是完全规整的,所有用过的内存放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点指示器,那所分配内存就是把指针向空闲空间那边移动一段与对象大小相等的距离。
空闲列表(使用CMS基于Mark-Sweep算法的收集器)
如果Java堆中的内存并不规整,已使用的内存和空闲的内存相互交错,虚拟机就必须维护一个列表,记录那些内存块可用,在分配的时候在列表上找到一块足够大的空间划分给对象实例,并更新列表记录。
选择哪种分配方式由java堆是否规整决定。Java堆是否规整由所采用的垃圾收集器是否带有压缩整理的功能决定。
4.初始化内存空间为零。
5.设置对象头中的信息。
6.调用对象构造函数进行初始化。
此时对象创建完成。
对象的内存布局
在HotSpot虚拟机中,对象在内存中存储的布局分为对象头(Header)、实例数据(Instance data)、对齐填充(Padding)三个部分。
对象头
一部分用于存储对象自身运行时的数据。(如哈希码、GC分代年龄、锁状态标志、线程持有锁、偏向线程ID、偏向时间戳等)
另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
如果对象是一个Java数组,那么对象头中必须有一块用于记录数组长度的数据。(因为Java虚拟机可以通过普通java对象的元数据信息确定java对象的大小,但是从数组的元数据中无法确定数组大小)
实例数据
对象真正存储的有效信息,
对齐填充
并不是必然存在,只是起到占位符作用,
Hotspot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,对象头正好是8字节的整数倍。
当对象实例数据部分没有对齐时,就需要通过对其填充来补充。
对象的访问方式
句柄访问
Java堆会划分一块内存作为句柄池,通过句柄访问。
直接指针
通过引用直接访问。