对象的创建
在语言层面,创建一个对象通常仅仅是一个new关键字而已。在虚拟机层面,虚拟机遇到一个new指令时,首先回去检查这个指令的参数是能在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已被加载,解析和初始化过。如果没有那么必须先执行类加载操作。
在类加载检查通过后,虚拟机将为对象从Java堆上划分出一块内存空间。有两种分配方式:
(1)指针碰撞
假设Java堆中的内存时规整的,所有用过的内存放在一边,没有用过的内存放在一边,中间放着一个指针作为分界的指示器,那么分配内存仅仅就是将指针向着未使用内存那边挪动一段与对象大小相等的距离,这种分配方式称为指针碰撞。
(2)空闲列表
Java中的内存空间不是规整的,已使用的内存和未使用的内存相互交错,虚拟机必须维护维护一张表,来记录那些内存块是可用的,在分配时从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。这种分配方式称为"空闲列表法"。
选择哪种分配方式,只要看Java堆是否规整,而Java堆是否规整又是由垃圾回收算法所决定的,因此在使用Serial,ParNew,等收集器时,系统采用的分配方法是:指针碰撞。而在使用CMS这种基于Mask-Sweep算法的收集器时采用空闲列表法。
还有一个需要考虑的问题就是对象创建在Java堆中是非常频繁的行为,因此在并发的情况下分配内存可能不是线程安全的。解决该问题有两种方案:
(1)对分配的内存空间动作进行同步处理,实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性。
(2)把内存分配动作按照线程划分在不同的空间中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(TLAB),哪个线程要分配内存就在哪个线程的TLAB上进行分配。
内存分配完成后,虚拟机将分配到的内存空间都初始化为零值(不包括对象头)。
接下来虚拟机要对对象进行必要的设置,例如该对象是哪个类的实例,如何才能找到类的元数据信息,对象的哈希码,对象的GC分代年龄信息。这些信息存放在对象的对象头。
在上面的工作都完成后,从虚拟机的角度来看一个新的对象已经产生。
但是从java程序角度看,对象的创建才开始-----