讨论篇:静态变量生命周期到底何如?如何使用?

3,441 阅读4分钟

静态变量的生命周期

​ 最近有个朋友问我个问题,为什么用静态变量作为一个标志位存储,但是时常数据丢失,有时候又是可行的,原因究竟为何。

public static boolean flag = false

​ 熟悉Java内存模型的朋友都知道,静态变量是存储在方法区之中的,而静态变量又属于整个类的实例,那么静态变量整个的生命周期是什么样子,什么时候会被回收呢?在此之前其实没有太多考虑这个问题,工作涉及到静态变量的地方一般也就是单例模式,但也没有出现过被回收的情况,加上工作以来,很多前辈同事也教导过不要使用太多的静态变量,所以,对这个问题自己也一直是一知半解,没有认真思考这个问题,在此参考了一些资料,做一些记录。

1.类的生命周期

上述也说道,静态变量是属于类的实例对象,所以静态变量的生命周期实际也就是类的生命周期。

当我们编写一个Java的源文件后,经过编译会生成一个后缀名为class的文件,这种文件叫做字节码文件,只有这种字节码文件才能够在Java虚拟机中运行,Java类的生命周期就是指一个class文件从加载到卸载的全过程。

一个Java类的完整的生命周期会经历加载、连接、初始化、使用、和卸载五个阶段,这里就不都进行讨论了,我们主要看加载和卸载两个阶段

1.1 加载

对于加载的时机,各个虚拟机的做法并不一样,但是有一个原则,就是当jvm“预期”到一个类将要被使用时就会在使用它之前对这 个类进行加载。比如说,在一段代码中出现了一个类的名字,jvm在执行这段代码之前并不能确定这个类是否会被使用到,于是,有些jvm会在执行前就加载这个类,而有些则在真正需要用的时候才会去加载它,这取决于具体的jvm实现。我们常用的hotspot虚拟机是采用的后者,就是说当真正用到一个类的时候才对它进行加载。

加载阶段是类的生命周期中的第一个阶段,加载阶段之后,是连接阶段。有一点需要注意,就是有时连接阶段并不会等加载阶段完 全完成之后才开始,而是交叉进行,可能一个类只加载了一部分之后,连接阶段就已经开始了。但是这两个阶段总的开始时间和完成时间总是固定的:加载阶段总是在连接阶段之前开始,连接阶段总是在加载阶段完成之后完成.

也就是类似懒加载的思想,在将要使用到的时候去加载这个类,这样也能避免浪费内存资源,当然,具体的实现就取决于具体的Jvm了

1.2 卸载

jvm卸载类的判定条件如下:参照hotspot虚拟机的一句话

1.该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。
2.加载该类的ClassLoader已经被回收。
3.该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。

从上面的条件可以看到,一个类被卸载的条件还是挺苛刻的,首先所有类的对象实例要被回收,二者,加载该类的ClassLoader要被回收,这个条件就基本等同于进程挂掉,最后是Class对象无任何引用。

从上面结论基本可以判断,静态变量的生命周期基本是从这个类加载,到整个进程死掉的过程,注意退出APK不代表杀死进程,这时候,静态变量还是有效的,进程重启后静态变量重新生成。依据根搜索算法,对象是否会被垃圾收集与未被使用时间长短无关,仅仅在于这个对象是不是“活”的。

同时我们也应该注意到,静态变量生命周期的如此之长,导致静态变量基本很难被GC回收掉,是非常占用内存的,所以在开发过程中,尽量少使用一些静态属性的变量,减少内存占用。

以上也是仅代表个人意见,期待各位朋友的发言