哪些内存需要回收?
JVM的内存结构包括五大区域:程序计数器,虚拟机栈,本地方法栈,堆区,方法区
程序计数器,虚拟机栈,本地方法栈:随线程生灭,不用考虑回收。
堆区,方法区:内存分配和回收是动态的,是垃圾收集器关注的部分
在回收对象之前,首先要判断是否存活,需要一些算法来分析。
1 引用计数算法
1.1算法分析
引用计数是垃圾收集器中的早期策略。
每个对象都有一个引用计数器,当任何一个变量被赋值为这个对象的引用时,计数加1。
当一个对象的某个引用超过的生命周期,或者被赋一个新值时,计数减1。
当一个对象的引用计数器为0时,就看可以被当做垃圾收集。
当一个对象被垃圾回收时,它引用的任何对象的计数器都减1。
1.2优缺点
优点:引用计数器可以很快地执行,交织在程序运行中,对程序不能被长时间打断的实时环境比较有利。
缺点:无法检测出循环引用。如,两个对象的某个成员变量分别引用这两个对象。如
MyObject object1 = new MyObject();
MyObject object2 = new MyObject();
object1.object = object2;
object2.object = object1;
object1 = null;
object2 = null;
则这两个对象不会被回收
2 可达性分析算法
程序把所有的引用关系看成是一张图,从一个节点GC Root开始,寻找对应的引用节点,找到这个节点之后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕后,剩余的节点就是无用的节点,判断为是可回收的对象。
在Java语言中,可作为GC Root的对象包括:
▲虚拟机栈(栈帧中的本地变量表)中引用的对象
▲方法区中类静态属性引用的对象
▲方法区中常量引用的对象
▲本地方法栈中JNI(即一般所说的Native方法)引用的对象
3 Java中的引用
无论是引用计数法,还是可达性分析,判断对象是否存活都与引用有关。
在Java语言中,将引用分为强引用,软引用,弱引用,虚引用四种,且引用强度依次减弱。
强引用:程序代码中普遍存在的,类似Object obj = new Object()的,只要强引用还在,垃圾收集器永远不会回收被引用的对象。
软引用:用来描述一些还有用但并非必需的对象。被软引用关联着的对象,在系统将要发生内存溢出之前,会进行回收,如果回收之后还没有足够的内存,才会抛出内存溢出异常。
弱引用:描述一些非必需的对象,只能生存到下一次垃圾收集发生之前,就会被回收。
虚引用:不影响对象的生存时间,也无法通过虚引用取得对象实例。它的作用是能在这个对象被垃圾收集器回收时收到一个系统通知。
4 回收前的挣扎
即使在可达性分析中不可达的对象,也不会被马上回收,至少需要经历两次标记过程。
第一次标记:可达性分析中不可达,没有与GC Roots相连接的引用链,将会被第一次标记。
第二次标记:第一次标记后会进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,则认为么有必要执行。如果在finalize()方法没有重新与引用链建立关联关系的,将会被进行第二次标记。
5 方法区的回收
方法区回收的内容:废弃常量和无用的类。
废弃常量也可通过引用的可达性分析来判断。
无用类的判断需要同时满足三个条件:
●该类的所有实例都已经被回收,即Java堆中不存在该类的任何实例。
●该类的加载器ClassLoader已经被回收
●该类的类对象(Class对象)没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
但满足这三个条件的无用类仅代表可以回收,但不是必然回收,这和对象回收不一样,是否对类进行回收,在HotSpot虚拟机中可以配置参数。