Java 对象在虚拟机中的创建过程
首先总结一张图:
可以看到还是比较复杂的。
这里简单总结下,具体的细节以后再分析:
在new 一个对象的时候,会发生如下操作:
首先是类加载,类加载会在Class第一次被引用的时候加载,类加载分为三个大部分:
加载 : 通过环境变量加类的全量路径查找该类
连接
验证: 验证class文件的合法性
准备:为静态变量分配内存,并设置初始值
解析:将符号引用替换为直接引用
初始化:初始化静态变量
总体来说,类加载就是用来加载Class对象的步骤。
总体分为三个步骤,加载,连接,初始化,其中连接又可以分为验证,准备,解析
静态变量的内存分配在连接操作的准备阶段,静态变量的初始化阶段在类加载阶段的初始化阶段。
然后是分配内存,注意这里的内存分配是为所实例化的对象分配内存,也就是对象的属性,类属性已经在类加载过程中的准备阶段分配并初始化。
内存分配包含两种方式:若是连续内存则使用指针碰撞方式,若是不连续内存则使用空闲列表的方式分配
在内存分配中存在线程安全的问题,解决方式为:在每次开启线程的时候,都为每个线程分配一段独立的空间,线程所需要分配的内存都在该空间分配(TLAB),当线程空间使用完毕的时候,使用CAS锁再次分配内存。
内存分配完毕后则进行内存初始化,内存初始化的作用是将内存都初始化为默认值,比如基本数据类型为0,boolean为false,对象为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();
}
}