正文
在我们写代码的过程中,我们很容易、很方便的就可以写出一个类,定义一些属性和方法。当我们使用的时候,直接新建new一个对象,或者想跳过范型检查的话,我们还可以利用反射的方式来创建我们的对象。那么在对象创建的时候,都发生了哪些事以及我们使用对象的时候,应该注意什么呢?
对象的创建过程
通常对象的对创建以及使用分为大致分为下面的几个过程:
加载:当我们进行创建对象的时候,比如使用new的时候,首先将去检查这个指令的参数是否能在常量池中定位到 一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
连接:这个过程分为三步,分别是:验证、准备以及解析。在这个过程中,Java虚拟机会进行对该对象进行空间分配,设置初值等。设置对象头,实例数据的初值,以及对齐补充数据。
初始化:在此之前,虽然Java虚拟机已经帮助我们实现了对象的空间分配以及设置默认值等操作,但是对于一些有值的属性会在初始化的时候进行赋值,也就是执行init()方法。执行完初始化的方法后,我们的对象就创建成功了,可以为以后的各个操作提供对象的实例。
使用:使用也是我们操作对象的过程。
卸载回收:使用完毕之后,Java虚拟机会自动的帮助我们将不用的对象内存进行回收。
DCL与Volatile之间的纠葛
DCL我们都知道,是我们使用懒汉式创建我们单例对象时候格式,也就是双重的检验。但是他与Volatile有什么关系呢?
我们在前面可知,在我们对象创建的连接的过程,Java虚拟机已经给我们分配了对象实例的内存大小以及地址,这个时候还没有进行初始化。初始化后,我们的对象实例就可以使用了。但是在多线程并发的时候,由于我们Java虚拟机会优化我们的指令的执行顺序,也就是指令的重排序,在单线程来看,我们的运行结果是没有问题的了。但是多线程的情况下们很有可能会发生我们的对象没有初始化就被其他的线程应用了。这就是对象逸出。
volatile的出现解决了这个问题,因为volatile可以实现指令的内存可见行以及禁止指令的重新排序。实现的方式采用的是内存屏障。有效的解决了我们前面所说的对象没有创建完就使用的情形。