java对象的创建过程

  • 面试官:创建java对象有哪几种方式?
  • 小白:new、clone、反射、反序列化。
  • 面试官:那你知道 new 一个对象的时候,JVM 做了哪些事吗?说说具体的过程。
  • 小白:就是 new 一下,然后在堆分配内存 ……
  • 面试官:你先回家等通知吧。

所以你知道 new 一个对象的时候做了哪些事,具体过程是怎样的吗?其实主要经历了如下过程:

  • 检查类是否加载过;
  • 分配内存;

1. 检查类是否加载过:

在之前 JVM 系列文章中说过,类通过 ClassLoader 生成一个模板,这个模板放在方法区(1.7的实现叫永久代,1.8的实现叫元空间),这个模板就包含了类的结构信息,包括方法、属性、常量池等。 new 一个对象的时候,首先会检查是否已经生成了类的模板。如果有,就直接拿来用;如果没有,就先加载类生成类的模板。

2. 分配内存:

经过了第一步之后,就要为对象分配内存,这个过程在堆中进行。分配内存有两种方式,一个叫指针碰撞,一个叫空闲列表。至于具体用哪种方式,取决于堆内存是否连续。之前的 JVM 垃圾回收文章中说到过,如果采用标记清除算法进行垃圾回收,就会产生内存碎片,如果是用标记整理,就不会有内存碎片。如果没有内存碎片,就用指针碰撞,否则就用空闲列表。

  • 指针碰撞:用过的内存放一边,没用过的放另一边,中间有个指针作为分界线,采用该方式为对象分配内存时,只需要将指针向未用过的内存方向移动对象所需内存大小即可。

  • 空闲列表:有内存碎片的时候,虚拟机会维护一个列表,列表记录了哪些位置的内存是可用的,给对象分配内存时就会找一块够大的内存去分配,然后更新列表记录。

3. 初始化零值:

什么叫初始化零值?你有没有发现,我们在类中定义的成员变量,是不需要赋初始值也可以使用的,而局部变量,没进行初始化去使用就会报错。这是为什么呢?就是因为在对象的创建过程中有“初始化零值”这一步。比如定义了一个 int 类型的成员变量,拿来用的时候,默认值是0,而不是null,这就是初始化零值。

4. 设置对象头:

什么是对象头?JVM 在存储对象时,增加的一些标记字段,用于增强对象的功能,这就是对象头。java 对象头包括:

  • Mark word:存储对象自身的一些数据,比如 hashCode,gc 分代年龄等;

  • Klass pointer:存储指针,JVM 通过这个指针来确定该对象是哪个类的实例;

  • array length:如果对象是数组,那么还会存储数组的长度。

5. 执行init方法:

经过上面四个步骤,一个新的 java 对象就已经产生了,最后就是执行 init 方法,让对象按照程序猿的意愿,进行初始化。什么叫按照程序猿的意愿初始化?就是你 new 对象的时候传了哪些参数,属性值是什么。


内存分配的过程中,如何保证线程安全呢?JVM 采用 TLAB + CAS 的方式保证线程安全。

TLAB 就是为每个线程预先在伊甸园区分配一块内存,JVM 要给对象分配内存的时候,首先会用 TLAB,即预先分配的这块内存,如果不够,就用 CAS 一直重试。

推荐阅读更多精彩内容