堆里面存放着 Java 世界差点儿全部的对象实例,垃圾收集器在对堆进行回收前。第一件事情就是要确定这些对象之中哪些还存活,哪些已经死去。推断对象的生命周期是否结束有下面几种方法
引用计数法
详细操作是给对象加入一个引用计数器。每当有一个地方引用时。计数器的值就加 1,;当引用失效时。计数器就减 1。不论什么时刻计数器为 0 的对象就 是不可能再被使用的。客观的说引用计数器算法实现简单,判定效率也非常高,在大部分情况下他都是一个不错的算法。可是引用计数器有缺陷
举个简单的样例,对象 A 和对象 B 都有字段 instance,赋值命令 objA.instance = objB,objB.instance=objA, 除此之外。这两个对象再无不论什么引用,实际上这两个对象已经不可能再被訪问了,可是他们由于互相引用这对方。导致引用数据器不为 0,无法通知 CG 收集器收集他们。
- public class ReferenceCountingGC {
- public Object instance = null;
- private static final int _1MB = 1024 * 1024;
- // 这个成员属性的唯一意义就是占点内存,以便能在GC日志中看清楚是否被回收过
- private byte[] bigSize = new byte[2 * _1MB];
- public static void testGC {
- ReferenceCountingGC objA = new ReferenceCountingGC();
- ReferenceCountingGC objB = new ReferenceCountingGC();
- objA.instance = objB;
- objB.instance = objA;
- obj.A = null;
- obj.B = null;
- }
- }
通过 GC 日志。虚拟机并没有由于这两个对象互相引用就不回收他们。这从側面说明虚拟机不是通过引用计数器来推断对象是否存活。
可达性分析算法
这个算法的基本思路是通过一系列称为 "GC Roots" 的对象作为起始点,从这些节点開始向下搜索。搜索走过的路径称为引用链,当一个对象到 GC Roots 没有不论什么引用链相连。则证明此对象是不可用的。
Java 语言中。可作为 GC Roots 的对象包括
1、虚拟机栈(栈帧中的本地变量表)中引用的变量
2、方法区中类静态属性引用的对象
3、方法区中常量引用的对象
4、本地方法栈中 JNI(Native 方法)引用的对象
引用分析
假设 reference 类型的数据中存储的数值代表的是还有一块内存的起始地址,就称为这块内存代表着一个引用。这样的定义太过狭隘,一个对象在这样的定义下仅仅有被引用和没有被引用两种状态。对于怎样描写叙述一些 "食之无味。弃之可惜" 的对象就显得无能为力。我们希望能描写叙述这样一类对象,当内存空间足够时。则能够保存在内存之中。假设内存空间在进行垃圾回收后还是非常紧张,则能够抛弃这些对象。
在 JDK1.2 以后,Java 对引用进行了扩展。将引用分为强引用、软引用、弱引用、虚引用四种,强度依次递减。
1、强引用:程序中普遍存在。比如 Object obj = new Object(); 仅仅要强引用来,CG 永远不会回收
2、软引用:描写叙述一些还实用,可是并不是必要的对象。
对于软引用的对象。在系统将要发生内存溢出之前,会把这些对象列进回收范围之中进行二次回收,假设回收了还没有足够的内存将报出内存溢出,JDK1.2 之后提供了 SoftReference 类来实现软引用。
3、弱引用:也是描写叙述非必须对象,被弱引用的对象仅仅能生存到下一次垃圾收集之前。当垃圾收集器工作时,不管内存是否够用都会被回收,JDK1.2 之后提供了 WeakReference 类来实现弱引用。
4、虚引用:幽灵引用或者幻影引用,最弱。一个对象是否有虚无引用的存在。全然不会对其生存时间构成影响,也无法通过虚无引用来取得一个对象实例。
生存还是死亡
即使在可达性分析算法中不可达对象,也并不是是 "非死不可" 的,这时候他们临时出 "缓刑" 之中,要真正宣告一个对象死亡。至少要经历两次标记过程:假设对象在进行可达性分析后发现没有与 GC Roots 相连接的引用链,那他会被第一次标记而且进行一次筛查,筛查的条件是此对象是有有必要运行 finalize 方法。
当对象没有覆盖 finalize 方法,或者 finalize 方法已经被虚拟机调用过,虚拟机将这两种情况视为没有必要运行。
类的 Finalize 方法,能够告诉垃圾回收器应该运行的操作,该方法从 Object 类继承而来。在从堆中永久删除对象之前。垃圾回收器调用该对象的 Finalize 方法。注意,无法确切地保证垃圾回收器何时调用该方法,也无法保证调用不同对象的方法的顺序。即使一个对象包括还有一个对象的引用。或者在释放一个对象非常久曾经就释放了还有一个对象,也可能会以随意的顺序调用这两个对象的 Finalize 方法。假设必须保证採用特定的顺序,则必须提供自己的特有清理方法。
回收方法区
方法区即为 Hotspot 虚拟机中的永久代,永久代的垃圾收集主要回收两部分内容:废弃常量和没用的类。以常量字符串 "abc" 为例,已经进入常量池,可是当前系统没有不论什么一个 String 对象叫做 abc,换句话说就是没有不论什么 String 对象引用常量池中的 abc,假设此时发生内存回收,这个 abc 常量就会被系统清理出常量池。常量池中的其它类、接口、方法、字段的符号引用也与此相似。
判定一个常量是否是废弃常量比較简单,而判定一个类是否是没用的类的条件 要苛刻的多,没用的类要满足下面条件:
1、该类全部的实例都已经被回收,Java 堆中不存在该类的不论什么实例。
2、载入该类的 ClassLoader 已经被回收
3、该类的 Java.lang.class 对象没有在不论什么地方被引用。无法在不论什么地方通过反射訪问该类。
来源: http://www.bubuko.com/infodetail-2230559.html