Java时一门面向对象的编程语言,在Java程序运行过程中无时无刻都有对象被创建出来。那在虚拟机中对象如何而创建呢?
虚拟机遇到一条new指令时,将首先检查这个指令的参数能否子常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那就必须先执行相应地类加载过程。
在类加载检查通过以后,虚拟机将为新生对象分配内存。对象所需内存大小在类加载完成后便可完全确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆总划分出来。而内存划分又可分为两种方式。方式1、指针碰撞:假设Java堆中内存是绝对规整的,所有使用过的内存在一边,空闲的内存放在另一边,中间有一个指针作为分界点的指示器,那么所分配内存就仅仅是把那个指针向空闲空间那边挪动与对象大小相等的一段距离。方式2、空闲列表:如果Java堆中的内存不是规整的,而是交错分布的,那就不能简单地进行指针碰撞来分配了,虚拟机就必须来维护一个列表,记录那些内存时可以使用的,在分配的时候从列表总找到一块足够大的空间划分给对象,并更新列表上的记录。这两种方式的选取时根据Java堆是否规整来决定的,而Java堆是否规整又是由垃圾回收器是否带有压缩整理宫内来决定的。
除了如何划分空间外,另一个需要思考的问题是对象创建在虚拟机中是非常频繁的行为,即使仅仅修改一个指针的指向位置,在并发情况下也并不是线程安全的,可能出现正给对象A分配内存,指正还没有修改,对象B又同时使用原来的指针来分配内存的情况。解决这个问题也有两种方案。一是对分配聂村空间的动作进行同步处理——实际是虚拟机采用CAS配以失败重试的方式来保证更新操作的原子性;另一种是把内存分配的动作按照线程划分在不同的空间之间进行,即每个线程在Java堆中预先分配一块小内存,称为本地线程分配缓冲。线程分配内存时现在本地线程分配缓冲上分配,当本地线程分配缓冲用完并分配新的时才需要同步锁定。
内存分配完成后,虚拟机需要将分配的内存都初始化为零值。这一步保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型锁对应的零值。
以上工作完成后,在虚拟机来看,一个新的对象已经产生了 ,但是从Java程序角度来看,对象创建来看,对象创建才开始——init方法还没有执行,所有字段都还为零。所以,一般执行new指令之后会接着执行init方法,把对象按照程序员的意愿来进行初始化,这样一个真正可用的对象才算完全产生出来。