垃圾回收的算法之引用计数和跟搜索:
1.引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用他时,+1,引用失效-1,为0的时候则能回收。
注意:Java没有采用该算法来管理内存。 可以用下面的代码测试。 Org o1 = new Org();
Org o2 = new Org();
o1.parent = o2;
o2.parent = o1;
o1 = null;
o2 = null;
System.gc();
此时gc(),并没有回收o1和o2
2.根搜索算法:通过一些列名为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所有走过的路径称为引用链,
当一个GC Roots没有任何引用链相连时,则证明此对象是不可用的。
如图: obj4和obj5就没有在GC Roots的引用链中,则是可被回收的对象。(用QQ画的,有点丑莫怪)。
在JAVA中,可以作为GC Roots的对象包括一下几种:
1.虚拟机栈(栈帧中的本地变量表)中的引用的对象。
2.方法区的类静态属性引用的对象。
3.方法区的常量引用的对象。
4.本地方法栈(JNI)引用的对象。
再谈引用:
JDK1.2之前,JAVA中的引用定义得很传统:如果reference类型的数据中存储的数值代表的是另外一块内存中的起始地址就称
这块内存代表着一个引用。 定义很纯粹,也太过狭隘,这种情况下只有 被引用和没有被引用两种状态。
对于描述一些“食之无味弃之可惜”的鸡肋型对象,就无能为力了。我们当然希望描述这样的对象:内存足够,则保留在内存中,
内存不足时,则可抛弃这些对象。
JDK1.2之后,JAVA对引用的概念进行了扩充,将引用分为[强引用(Strong Reference)、软引用(Sofr Reference)、
弱引用(Weak Reference)、虚引用(Phantom Reference)] 引用强度依次减少。
一个对象死亡的过程:
对象的死亡,GC Roots找不到它后,它并非马上就死亡,而是进入一个 F-Queue的队列中,并在稍后由一条虚拟机自动建立的、
低优先级的Finalizer线程去执行死刑。此时这个对象才死亡,但可以通过重写finalize()方法,逃出生天。
值得注意的是:finalize()方法始终只会被执行一次,如果对象下一次面临回收的时候,就是神仙难救了。
虽然救人一命胜造七级浮屠,但一般情况下,不建议重现finalize()方法救该对象,相反还要尽量避免。他不是C/C++中的析构函数。
而是Java刚诞生时为了使C/C++程序员更容易接收它做的一个妥协。它的运行代价高昂,不确定性大,无法保证各个对象的调用顺序。
另外要注意的是:他不适合做关闭外部资源之类的工作,因为完全可以用try-finally来做会更好,更即时。所以,这个可以忘掉。
回收方法区:
Java虚拟机规范中说过,可以不要求虚拟机在方法区(HotSpot虚拟机中的永久代)中实现垃圾收集,而且在方法区进行垃圾收集的“性价比”一般较低。
永久代垃圾回收主要是回收:废弃的常量和无用的类。
回收废弃的常量与回收Java堆中的对象非常类似。
回收无用的类相对而言条件比较苛刻,需要同事满足以下3个条件:
1.该类的所有实例都已被回收,即Java堆中不存在该类的任何实例。
2.加载该类的ClassLoader已被回收。
3.该类对于的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
满足以上3个条件的无用类可以进行回收,仅仅只是可以,和对象不同,不使用了就必然回收。
HotSpot虚拟机提供了-Xnoclassgc参数进行控制,还可以使用-verbose:class以及-XX:+TraceClassLoading、
-XX:+TraceClassUnLoading查看类的加载和卸载信息。 -verbose:class和-XX:+TraceClassLoading可以在Product版虚拟机使用
-XX:+TraceClassUnLoading参数需要在fastdebug版的虚拟机支持。
值得注意的是:大量使用发射、动态代理、GCLib等bytecode框架的场景,以及动态生成JSP和OSGi这类频繁自定义ClassLoader
的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出。