尽管Java对象的内存分配可以选择在堆外进行,但是不可否认这仅仅只是为了降低GC回收频率以及提升GC回收效率的一种辅助手段,所以Java堆仍然是分配/存储对象实例的主要区域。JVM中包含三种引用类型:
1、类类型;
2、数组类型;
3、接口类型;
这些引用类型的值分别由类实例、数组实例、和接口的派生类实例负责动态创建。如果是在语法层面上,创建一个对象实例无非就是使用new关键字即可。
简单来说,当语法层面使用new关键字创建一个Java对象的时候,JVM首先会检查这个new指定的参数是否在常量池中定位到一个类的符号引用,然后检查与这个符号引用对应的类是否已经成功经历过加载、解析、初始化。完成装载步骤以后,就已经完全可以确定创建对象实例所需要的内存空间大小了。JVM会为其进行内存分配,以存储生成的对象实例。
基于线程安全考虑,如果一个类在分配内存前已经成功完成类装载,那么JVM会优先选择在thread local allocation中为对象实例分配内存空间。TLAB在Java堆区中是一块线程私有区域,它包含在Eden控件中,除了可以避免一系列的非线程安全问题外,同时还能够提升内存分配的吞吐量,因此我们可以将这种内存分配方式称之为快速分配策略。不过TLAB的空间是很小的,仅是Eden空间的1%。一旦分配失败,则在Eden空间中分配。如果Eden也无法分配,那么就会进行GC,直到可以分配为止。(如果是大对象,则直接在老年代分配);
当前对象成功分配内存以后,就会初始化对象。JVM首先会进行零值初始化,确保了对象实例字段可以不用赋值就可以使用。
零值初始化以后,JVM就会初始化对象头和实例数据。
最后,将对象的引用入栈,再更新PC寄存器中的字节码指令地址。
经过以上一系列步骤以后,Java对象才算是真的创建成功。