简单总结Java对象在虚拟机中的创建过程

Java 对象在虚拟机中的创建过程

首先总结一张图:

image

可以看到还是比较复杂的。

这里简单总结下,具体的细节以后再分析:

在new 一个对象的时候,会发生如下操作:

首先是类加载,类加载会在Class第一次被引用的时候加载,类加载分为三个大部分:

  • 加载 : 通过环境变量加类的全量路径查找该类
  • 连接
    • 验证: 验证class文件的合法性
    • 准备:为静态变量分配内存,并设置初始值
    • 解析:将符号引用替换为直接引用
  • 初始化:初始化静态变量

总体来说,类加载就是用来加载Class对象的步骤。

总体分为三个步骤,加载,连接,初始化,其中连接又可以分为验证,准备,解析

静态变量的内存分配在连接操作的准备阶段,静态变量的初始化阶段在类加载阶段的初始化阶段。

然后是分配内存,注意这里的内存分配是为所实例化的对象分配内存,也就是对象的属性类属性已经在类加载过程中的准备阶段分配并初始化。

内存分配包含两种方式:若是连续内存则使用指针碰撞方式,若是不连续内存则使用空闲列表的方式分配

在内存分配中存在线程安全的问题,解决方式为:在每次开启线程的时候,都为每个线程分配一段独立的空间,线程所需要分配的内存都在该空间分配(TLAB),当线程空间使用完毕的时候,使用CAS锁再次分配内存。

内存分配完毕后则进行内存初始化,内存初始化的作用是将内存都初始化为默认值,比如基本数据类型为0,booleanfalse,对象为null

接下来最后一步是执行构造函数,为成员属性附上初始值,这里值得注意的一点便是为对象设置默认值有两种方式,第一种是在声明的时候初始化,第二种是只声明,具体的值在构造函数中初始化:

private int a=0;
private int b;

public Test(){
    b=2;
}

但是经过编译后的class文件你会发现,声明时初始化其实就是一个语法糖,它的具体初始化还是在构造函数中。

总结可得:对象创建过程为:先初始化类对象,再初始化实例。类对象中静态变量的内存分配阶段为类加载的准备阶段,初始化阶段为类加载中的初始化阶段。接下来便是实例的内存分配,赋默认值,最后执行构造函数。

问题:

看完上面的总结,思考下面的程序输出什么?为什么?

public class Test(){
    public static Test instance=new Test();
    private int a;
    private static int b=2;

    public Test(){
       a++;
       System.out.printf("a: %d  b: %d \n",a,b);   
    }



    public static void main(String args[]){
        Test t=new Test();
    }
}