在过往的文章中主要讲述了JVM-Java虚拟机内存模型,接下来我们讲解一下Java对象在虚拟机中是如何判断存亡的,如何回收已经消亡的对象的呢。也就是Java对象存活算法及垃圾回收算法
在Java虚拟机中主要关注的也就是Java堆中的对象,因为这里面存放了Java世界中几乎所有的对象实例,要想对已经失去意义的对象也就是已经消亡的对象进行回收之前首先就要判断, 哪些对象还“存活”着,哪些对象已经“死去”(“死去”即不可 能再被任何途径使用的对象)了。 说到这里就不得不引出两个比较重要的算法:
在很多教科书素材中都是这样提到的:会在对象中添加一个引用计数器,每当有一个地方引用它时,该计数器就会加一,当引用失效时,该计数器就会减一;任何计数器为零的时候即是意味着该对象不再被使用。引用计数算法其实效率是很高的,只不过是占用了一小部分空间,原理也比较简单。该算法也在很多知名的应用语言中有所应用比如:COM(Component Object Model)技术、使用 ActionScript 3 的 FlashPlayer、Python 语言以及在游戏脚本领域得到许多应用的 Squirrel 中都使用了引用 计数算法进行内存管理。
话说回来,Java判断对象的存活就是根据引用计数算法吗?你觉得呢?实践出真知:通过配置**VM参数: **-XX:+PrintGCDetails运行以下代码:
public class ReferenceCountingGC {
public Object instance = null;
private static final int _1MB = 1024 * 1024;
private byte[] bigSize = new byte[2 * _1MB];
/**
* 这个成员属性的唯一意义就是占点内存,以便能在 GC 日志中看清楚是否有回收过
*/
public static void testGC() {
ReferenceCountingGC objA = new ReferenceCountingGC();
ReferenceCountingGC objB = new ReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;
// 假设在这行发生 GC,objA 和 objB 是否能被回收?
System.gc();
}
public static void main(String[] args) {
testGC();
// System.out.println("paidaxing +++++++++++++++++++++++");
// try {
// Thread.sleep(500000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
}
控制台数据如下:

从运行结果中可以清楚看到内存回收日志中包含“840K->0K”,意味着虚拟机并 没有因为这两个对象互相引用就放弃回收它们,这也从侧面说明了 Java 虚拟机并不是通 过引用计数算法来判断对象是否存活的。
关于gc日志观察Java内存分配与回收策略可参考文章:实战-通过gc日志观察Java内存分配与回收策略
当前主流的商用程序语言(Java、C#、以及古老的Lisp)的内存管理子系统都是通过可达性分析( Reachability Analysis )算法来判断对象是否存活的。其主要原理是通过一系列被称之为“GC Roots”的根对象作为起始节点集从这些节点开始,根据引用关系向下搜索,搜索过程的路径称之为“引用链”( Reachability Analysis );如果某个对象到达“GC Roots”没有任何的引用链相连,或者用图论的话来说就是从“GC Roots”到这个对象不可达时,则证明该对象就不会再被使用的。如图所示:对象 object 5、object 6、object 7 虽然互有关联,但是它们到 GC Roots 是不可达的,因此它们将会被判定为可回收的对象 。

上面一直在说“GC Roots” 那么在Java技术体系中什么可以做为GC Roots呢?
以上就是主要的可以作为“GC Roots”的对象,当然除了这些固定的“GC Roots”集合外,还有其它对象临时性的加入 共同构成完整 GC Roots 集合。
无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象 是否引用链可达,判定对象是否存活都和“引用”离不开关系。关于引用的概念在JDK1.2前后有所不同 JDK 1.2 之前
如果 reference 类型的数据中存储的数值代表的是另外一块 内存的起始地址,就称该 reference 数据是代表某块内存、某个对象的引用
JDK 1.2 之后 :
将引用划分了:
强引用(Strongly Re-ference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)4 种,这 4 种引用强度依次逐渐减弱。
是最传统的引用定义,是指程序代码之间普遍存在的引用赋值 ,即类 似“Object obj=new Object()”这种引用关系。 所以无论任何情况下:只要强引用存在,垃圾收集器就永远不会回收被强引用的对象。
是用来描述一些还有用,但是非必须的对象。只被软引用关联的对象,在系统发生内存溢出异常之前,会将这些对象列进回收范围之内进行二次回收,如果这次回收还是没有足够的内存,才会出现内存溢出的异常。JDK1.2版本之后使用SoftReference类来实现软引用。
也是用来描述一些非必须的对象,但是它的强度还要毕软引用弱一些,被弱引用关联的对象只能存活到下一次垃圾收集发生为止,当垃圾收集器开始工作,无论是内存是否足够,都会回收掉被弱引用关联的对象。在JDK1.2版本之后使用WeakReference类来实现软引用。
虚引用又被称为幽灵引用、幻影引用。它是一种最弱的引用关系。一个对象是否虚引用的存在,完全不对其生存时间构成影响,也无法通过一个虚引用取得一个对象的实例。设置虚引用的目的就是:在这个对象被垃圾收集器回收时收到一个系统的通知。JDK1.2版本之后,使用PhantomReference 类来实现虚引用。
讲完引用是否对一个对象的存活依据有了一些概念呢?那你觉得Java中判断对象中存活的依据仅是这些吗?当然不是,参考《深入理解Java虚拟机》中有这样一句话:

以上就是关于在Java中如何判断一个对象是否存活,以及通过什么方式判断存活,你了解了吗?为了避免篇幅过长在下一篇中讲解:JVM是如何回收已经消亡的对象的。