对象的创建
在Java中创建对象通常只是一个 new 关键字,当Java虚拟机遇到一条 new 的字节码时首先会去检查这个指令的参数是否能在常量池中定位到一个类的引用,并检查这个符号引用代表的类是否已经被加载、解析和初始化过,如果没有则执行相应的类初始化流程。类加载检查通过后,虚拟机将为新生对象分配内存,对象内存大小在类加载后可完全确定。
指针碰撞:加入堆被划分成两块,一块是空闲的堆,另一块是已使用的堆,对象分配内存只需要将已使用的内存像空暇内存进行扩充,如果两块内存交织一起则无法进行简单的指针碰撞。
空闲列表:在虚拟机中维护一个列表记录哪些内存可用,在可用内存中找到一块足够大的内存空间,提供对象的存储。
对象的创建是频繁的行为,即使修改一个指针指向的位置,在并发情况下也是线程不安全的。例如:正在给对象A分配空间,对象B使用同样的指针进行分配空间。解决方案一般分为两种;一种是做同步处理,实际上虚拟机采用cas进行自旋失败重试保证原子性,另一种则是按照线程将对象的分配划分在不同的空间下,也就是在堆中预先分配内存,只有当本地缓存用完后才需要做同步处理。第二种也被成为TLAB,虚拟机是否采用TLAB 通过-XX:+/- UserTLAB参数设定。
内存分配完毕后会将内存空间初始化为0,如果使用了TLAB也可以提前进行。这步操作保证了Java代码中可以不赋值就直接使用。
之后Java虚拟机还要对对象进行一些设置列入:对象所属的实例,哈希码,分代年龄等,这些信息被保存在Object Head 对象头中。
最后后调用对象的构造函数。
对象的内存分布
对象在内存中存储布局可以划分为三个部分:对象头、实例数据和对象填充。如上所属对象头存放一些必要的信息(Mark Word),另一部分则是类型指针,即对象指向它元数据的指针,Java虚拟机通过这个指针判断该对象是哪个类的实例。实例数据则是对象真正存储的有效信息,也就是在类中定义的各种字段,也包括从父类继承的字段。对其字段则没有特殊的含义,在hotspot虚拟机中要求对象的起始地址必须为8的整数倍,因此对象的大小也同样是8的整数倍。若对象头+实列信息不是8的整数倍则进行对齐。