Java是一门面向对象的语言,创建对象通常仅仅是一个new关键字而已,而在虚拟机中,对象的创建又是怎样的一个过程呢?

Java对象创建过程

java 对象是如何创建的 java对象创建过程详解_Java

虚拟机遇到一个new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有那么必须先执行相应的类加载过程。

在类加载检查通过后,接下来虚拟机将为新生对象分配内存(所需内存大小在类加载完成后便可以完全确定)。为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。

除了如何划分可用空间之外,还需要考虑到并发情况是否为线程安全。

内存分配完成后,虚拟机需要将分配到的内存空间初始化为零值(不包括对象头),如果使用TLAB,这一过程也可以提前至TLAB(Thread Local Allocation Buffer)分配时进行。

虚拟机堆对象进行必要的设置,比如该对象是哪个类的实例、如果才能找到类的元数据信息,对象的GC分代年龄等信息。

上面的过程完成后:

  • 从虚拟机的视角来看,一个新的对象已经产生
  • 从Java程序视角来看,对象创建刚刚开始

JVM分配内存的过程

java 对象是如何创建的 java对象创建过程详解_JVM_02

JVM分配内存的原则主要是Java堆中是否有绝对规整的内存,即所有用过的内存放一边,空闲的内存放一边。

  • 指针碰撞(Bump the Pointer):在使用过的内存还空闲的内存中间存放一个指针作为分界点的指示器,需要分配内存时,只需要将那个指针向空闲内存中间移动与对象大小相等的距离便可。
  • 空闲列表(Free List):维护一张表,用于记录那些内存块是可用的,在分配的时候,从列表中找到一块足够大的空间划分给实例,并更新列表上的记录。

Java堆是否规整取决于垃圾收集器是否带有压缩整理功能。

因此在使用Serial, ParNew等带有Compact过程的收集器时,系统采用的分配算法是指针碰撞;
而使用CMS这种基于Mark-Sweep算法的收集器时,通常采用空闲列表。

JVM对线程安全的处理

在并发情况下,如果创建的对象不是线程安全,JVM目前有两种解决方案。

  • 对分配空间的动作进行同步处理:即采用CAS配上失败重试的方式保证更新操作的原子性;
  • 把内存分配的动作按照线程划分在不同的空间之中进行:即每个线程在Java堆中预先分配一小块内存,称为TLAB,哪个线程要分配内存,就在哪个TLAB上分配,只有TLAB用完并分配新的TLAB时,才需要同步锁定。

虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定。