在java当中一个对象被创建,再被引用指向,然后参与一系列的逻辑计算,到最后被垃圾回收掉,这个过程是怎样的,对我们开发来说也是极其重要的
对象的生命周期
对象在JVM当中的生存周期一般都是从
创建(created)-->应用(in use)-->不可见(invisiable)-->不可达(unreachable)-->收集(collected)-->终结 (finalized)-->对象空间重分配(de-allocated)
接下来介绍每个阶段主要的做什么的
创建阶段
在创建阶段,系统需要完成下面的一系列操作
- 为对象分配存储空间
- 开始构建对象
- 从父类到子类对static成员进行初始化
- 父类成员变量按照顺序进行初始化,递归调用父类构造方法
- 子类成员变量初始化,调用子类构造方法
以上步骤执行结束,并且有引用指向这个对象,那么这个对象就进入到了下一个阶段使用阶段(In-use)
应用阶段
关于应用阶段,系统至少需要维护一个对象的强引用,当然关于弱引用,软引用和虚引用我们后面的文章会进行介绍
不可见阶段
当程序处于不可视阶段说明程序当中不再持有对象的任何强引用,简单来说就是,程序的执行超出了对象的作用域了
public void process () {
try {
Object obj = new Object();
obj.doSomething();
} catch (Exception e) {
e.printStackTrace();
}
while (isLoop) { // ... loops forever
// 这个区域对于obj对象来说已经是不可视的了
// 因此下面的代码在编译时会引发错误
obj.doSomething();
}
}
如果一个对象已经使用完了,在其可视区域不需要再使用到了,那么就可以把它指向null, 如上面的代码,可以在obj.doSomething()之后,将对象obj=null, 这样方便jvm及时对这个对象进行垃圾回收
不可达阶段
这个阶段是指,在虚拟机所管理的对象引用根集合当中再也找不到直接或者间接的强引用,这些对象通常是指,所有线程栈当中的临时变量,所有已装载的类的静态变量或者对本地代码接口(JNI)的引用。这些对象都是准备被垃圾回收的对象,并不能直接对垃圾回收直接回收。
收集阶段
当垃圾回收器发现对象处于不可达阶段,并且垃圾回收器已经做好了内存重新分配的准备,则对象进入收集阶段,如果该对象重写了finalize方法,则会去执行finalize方法
(注:不建议重写finalize方法,会影响jvm对象分配和回收速度,原因是,在分配对象时,会在垃圾回收器上注册该对象,以便在垃圾回收时能执行finalize方法,在方法的执行时,需要消耗CPU,其次,在finalize方法当中可能持有其他的强引用持有该对象,会造成对象的复活,由收集阶段变为应用阶段,不利于后面的代码管理)
终结阶段
当执行完finalize方法之后,对象仍然处于不可达状态,则对象进入到终结阶段,等待垃圾回收器对该对象的空间进行回收
重新分配
垃圾回收器对该对象所占用的空间进行了回收或者重新分配,这个对象在程序当中彻底结束了,称之为对象空间重新分配阶段
类的生命周期包括以下几个阶段,加载--》连接--》初始化--》使用--》卸载,下面我们分别对每个阶段所做的事情进行一个分析
加载阶段
当我们编写好的java文件编译成.class文件,jvm识别的就是.class这种字节码文件,类的生命周期其实就是从jvm加载class文件到最终消亡的过程,加载其实是将,class文件找到类的信息,将其加载到方法区当中,然后在内存当中生成一个java.lang.Class对象,作为方法区当中这个类的入口,
关于加载时机,每个虚拟机的做法是不同的,但是有个原则就是当预期到某个类,需要被用到的时候,会在使用之前对这个类进行加载,比如在某个代码当中出现了一个类的名字,有的虚拟机选择去加载,而有的虚拟机会在真正用到的时候再去加载,hotspot采用的是后者。
连接
连接阶段有分为三个小阶段,分别是,验证、准备、解析,连接阶段和加载阶段是交叉运行的,但是加载阶段一定是早于连接阶段开始的,连接阶段一定是晚于加载阶段结束的
验证阶段:确定该类是否符合jvm规范,有没有属性和行为的重复,继承是否合理,总之就是保证jvm能够运行加载的类
准备阶段:主要是为类的静态变量分配内存,并设置他们jvm的默认值,对于非静态变量,则不会分配内存,静态变量的初始值并不是程序当中设置的,而是每个类型的默认初始值,比如int,long之类的默认值是0,引用类型默认值是null,常量的值就是我们程序当中设定的值。
解析:把常量池当中的符号引用解析为直接引用,就是将所有的类或者接口,字段名,方法名转换为内存地址
初始化
这个阶段就是把静态变量赋值的过程,执行顺序,父类静态域,静态代码块,子类静态域,静态代码块
使用
在类的使用阶段,又分为三小阶段,分别是对象实例化、垃圾回收、对象终结
对象实例化:执行类当中的构造方法,如果该类有父类,jvm会执行父类的构造方法,在堆内存当中为父类开辟内存空间,并设置默认值,然后根据构造函数的代码将真正的赋予实例变量,然后引用变量获取对象的首地址,同过操作对象来调用实例变量和方法
垃圾回收:当对象不再被使用的时候,就会被虚拟机标记上记号,在堆中等待gc回收
对象终结:当对象被gc回收之后,对象就不存在了
卸载
当满足以下条件的时候,类就会被卸载掉
- 该类的所有实例都已经被回收,在java堆中不存在这个类的任何实例
- 加载该类的ClassLoader被回收
- 该类对应的java.lang.Class对象没有被任何地方引用,无法通过反射访问该类
满足以上条件,jvm就会在方法区垃圾回收时,对类进行卸载,类的卸载就是清空方法区类的信息。
参考: