静态变量的生命周期,起始于类的加载,终止于类的释放。
什么时候类会加载呢?
我们知道,在app打开时,会创建一个进程,然后初始化一个dvm的实例,负责类的加载释放 和 垃圾回收等。
换句话说,在进程创建之后,就会加载类,静态变量诞生了。
那何时释放?
当然是在类卸载的时候。同上面。在进程结束之前,静态变量就寿终正寝。
我们知道,Android中,你是不知道何时进程会被Kill。所以
1.不能保证静态变量会一直存在.(进程可能被Kill掉)
2.每次打开app时静态变量的值都是初始值(进程没有被kill掉所以静态变量保存的还是上次的值)。
而且,静态变量是不会被垃圾回收的,其对象一直保持引用,及ARC不可能是0。
所以要自己释放静态变量。
网上查到的相关链接
单例模式讨论篇:单例模式与垃圾回收:http://blog.csdn.net/zhengzhb/article/details/7331354
Jvm的垃圾回收机制到底会不会回收掉长时间不用的单例模式对象,这的确是一个比较有争议性的问题。将这一部分内容单独成篇的目的也是为了与广大博友广泛的讨论一下这个问题。为了能让更多的人看到这篇文章,请各位博友看完文章之后,点一下“顶”,让本篇文章排名尽量的靠前。笔者在此谢过。
讨论命题:当一个单例的对象长久不用时,会不会被jvm的垃圾收集机制回收。
首先说一下为什么会产生这一疑问,笔者本人再此之前从来没有考虑过垃圾回收对单例模式的影响,直到去年读了一本书,《设计模式之禅》秦小波著。在书中提到在j2ee应用中,jvm垃圾回收机制会把长久不用的单例类对象当作垃圾,并在cpu空闲的时候对其进行回收。之前读过的几本设计模式的书,包括《java与模式》,书中都没有提到jvm垃圾回收机制对单例的影响。并且在工作过程中,也没有过单例对象被回收的经历,加上工作中很多前辈曾经告诫过笔者:尽量不要声明太多的静态属性,因为这些静态属性被加载后不会被释放。因此对jvm垃圾收集会回收单例对象这一说法持怀疑态度。渐渐地,发现在同事中和网上的技术人员中,对这一问题也基本上是鲜明的对立两派。那么到底jvm会不会回收长久不用的单例对象呢。
对这一问题,**笔者本人的观点是:不会回收**。
下面给出本人的测试代码
class Singleton { private byte[] a = new byte[6*1024*1024]; private static Singleton singleton = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return singleton; } } class Obj { private byte[] a = new byte[3*1024*1024]; } public class Client{ public static void main(String[] args) throws Exception{ Singleton.getInstance(); while(true){ new Obj(); } } }
本段程序的目的是模拟j2ee容器,首先实例化单例类,这个单例类占6M内存,然后程序进入死循环,不断的创建对象,逼迫jvm进行垃圾回收,然后观察垃圾收集信息,如果进行垃圾收集后,内存仍然大于6M,则说明垃圾回收不会回收单例对象。
运行本程序使用的虚拟机是hotspot虚拟机,也就是我们使用的最多的java官方提供的虚拟机,俗称jdk,版本是jdk1.6.0_12
运行时vm arguments参数为:-verbose:gc -Xms20M -Xmx20M,意思是每次jvm进行垃圾回收时显示内存信息,jvm的内存设为固定20M。
运行结果:
……
[Full GC 18566K->6278K(20352K), 0.0101066 secs]
[GC 18567K->18566K(20352K), 0.0001978 secs]
[Full GC 18566K->6278K(20352K), 0.0088229 secs]
……
从运行结果中可以看到总有6M空间没有被收集。因此,笔者认为,至少在hotspot虚拟机中,垃圾回收是不会回收单例对象的。
后来查阅了一些相关的资料,hotspot虚拟机的垃圾收集算法使用根搜索算法。这个算法的基本思路是:对任何“活”的对象,一定能最终追溯到其存活在堆栈或静态存储区之中的引用。通过一系列名为根(GC Roots)的引用作为起点,从这些根开始搜索,经过一系列的路径,如果可以到达java堆中的对象,那么这个对象就是“活”的,是不可回收的。可以作为根的对象有:
虚拟机栈(栈桢中的本地变量表)中的引用的对象。
方法区中的类静态属性引用的对象。
方法区中的常量引用的对象。
本地方法栈中JNI的引用的对象。
方法区是jvm的一块内存区域,用来存放类相关的信息。很明显,java中单例模式创建的对象被自己类中的静态属性所引用,符合第二条,因此,单例对象不会被jvm垃圾收集。
虽然jvm堆中的单例对象不会被垃圾收集,但是单例类本身如果长时间不用会不会被收集呢?因为jvm对方法区也是有垃圾收集机制的。如果单例类被收集,那么堆中的对象就会失去到根的路径,必然会被垃圾收集掉。对此,笔者查阅了hotspot虚拟机对方法区的垃圾收集方法,jvm卸载类的判定条件如下:
该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。
加载该类的ClassLoader已经被回收。
该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。
只有三个条件都满足,jvm才会在垃圾收集的时候卸载类。显然,单例的类不满足条件一,因此单例类也不会被卸载。也就是说,只要单例类中的静态引用指向jvm堆中的单例对象,那么单例类和单例对象都不会被垃圾收集,依据根搜索算法,对象是否会被垃圾收集与未被使用时间长短无关,仅仅在于这个对象是不是“活”的。假如一个对象长久未使用而被回收,那么收集算法应该是最近最长未使用算法,最近最长未使用算法一般用在操作系统的内外存交换中,如果用在虚拟机垃圾回收中,岂不是太不安全了?以上是笔者的观点。
二(转载)Android静态变量的生命周期
Android是用Java开发,其静态变量的生命周期遵守Java的设计。我们知道静态变量是在类被load的时候分配内存的,并且存在于方法区。当类被卸载的时候,静态变量被销毁。在PC机的客户端程序中,一个类被加载和卸载,可简单的等同于jvm进程的启动和结束。那么在Android中呢?用的Dalvik vm也是一样的。不过Android不太突出的进程概念,所以对静态变量的生命周期就会感觉模糊,这种模糊对于值类型是无所谓的,如果是静态的对象引用,则与内存回收、内存泄漏这些问题有关,有必要加深研究和理解。
一、静态变量在类被加载的时候分配内存。
类在什么时候被加载?
当我们启动一个app的时候,系统会创建一个进程,此进程会加载一个Dalvik VM的实例,然后代码就运行在DVM之上,类的加载和卸载,垃圾回收等事情都由DVM负责。也就是说在进程启动的时候,类被加载,静态变量被分配内存。
二、静态变量在类被卸载的时候销毁。
类在什么时候被卸载?
在进程结束的时候。
说明:一般情况下,所有的类都是默认的ClassLoader加载的,只要ClassLoader存在,类就不会被卸载,而默认的ClassLoader生命周期是与进程一致的,本文讨论一般情况。
三、Android中的进程什么时候结束
这个是Android对进程和内存管理不同于PC的核心——如果资源足够,Android不会杀掉任何进程,另一个意思就是进程随时可能会被杀掉。而Android会在资源够的时候,重启被杀掉的进程。也就是说静态变量的值,如果不做处理,是不可靠的,可以说内存中的一切都不可靠。如果要可靠,还是得保存到Nand或SD卡中去,在重启的时候恢复回来。
另一种情况就是不能把退出所有Activity等同于进程的退出,所以在用户点击图标启动应用的时候,以前存放于静态变量中的值,有可能还存在,因此要视具体情况给予清空操作。
四、Application也是一样不可靠
Application其实是一个单例对象,也是放在内存中的,当进程被杀掉,就全清空了,只不过Android系统会帮重建Application,而我们存放在Application的数据自然就没有了,还是得自己处理。
五、静态引用的对象不会被垃圾回收
只要静态变量没有被销毁也没有置null,其对象一直被保持引用,也即引用计数不可能是0,因此不会被垃圾回收。因此,单例对象在运行时不会被回收。