Java是一门面向对象的编程语言,在Java程序运行过程中不断有对象被创建出来。

虚拟机遇到一条new指令时,会进行一系列对象创建的操作:

1.检查常量池中是否有即将要创建的这个对象所属的类的符号引用;

● 若常量池中没有这个类的符号引用,说明这个类还没有被定义!抛出ClassNotFoundException;

● 若常量池中有这个类的符号引用,则进行下一步工作;

2.进而检查这个符号引用所代表的类是否已经被JVM加载;

● 若该类还没有被加载,就找该类的class文件,并加载进方法区;

● 若该类已经被JVM加载,则准备为对象分配内存;

3.根据方法区中该类的信息确定该类所需的内存大小;

●  一个对象所需的内存大小是在这个对象所属类被定义完就能确定的!且一个类所生产的所有对象的内存大小是一样的!JVM在一个类被加载进方法区的时候就知道该类生产的每一个对象所需要的内存大小。

4.在类加载检查通过之后,虚拟机从堆中划分一块对应大小的内存空间给新的对象;

划分空间给对象分为两种方法:

1.指针碰撞:

如果Java堆中的内存是绝对规整的,所有用过的内存放在一边,空闲的部分放在另一边,中间放着一个指针作为分界指示器。分配内存时仅仅只是将指针向空闲区域移动跟对象大小相等的距离即可,这种方法就是“指针碰撞”。如果JVM的垃圾收集器采用复制算法或标记-整理算法,便会采用这种分配方式。

2.空闲列表:

如果Java堆中的内存不是规整的,已使用的部分和空闲部分相互交错,那就没法用“指针碰撞”分配内存了,虚拟机就要维护一个表,记录哪些内存块是可用的,分配的时候从列表中找到足够大的一块空间给对象,并更新表的记录。这种分配方式就是“空闲列表”。如果JVM的垃圾收集器采用标记-清除算法,虚拟机就会采用这种分配方式。

总结一句话,对象在Java堆中的分配方式取决于虚拟机指定的垃圾回收方式,即垃圾收集器。

5.为对象中的成员变量赋上初始值(默认初始化);

6.设置对象头中的信息;

7.调用对象的构造函数进行初始化 。

此时,整个对象的创建过程就完成了。


除了划分可用的空间意外,还要考虑在并***况下线程安全性问题。对于这类问题,解决方法有两个:

1.一对分配内存空间的动作进行同步处理——实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性。

2.把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer , TLAB)。哪个线程分配内存,就在该线程的TLAB上分配,当TLAB上的空间不足时,虚拟机会分配新的TLAB,并对分配过程进行同步。