后面在 jvm 组成构造一文中,说到了 GC 和一些算法,那么在这篇文章里,就具体说说 GC 的罕用算法。
垃圾回收 (Garbage Collection,GC),就是将垃圾回收,防止过于占用内存空间,导致内存透露,对内存堆中曾经死亡或者长时间没有应用的对象进行革除和回收。
既然是垃圾回收,那么如何判断哪些对象是垃圾,须要被回收呢?
如何找到程序里的垃圾
(1)援用计数法
援用计数法其实就是给每个对象增加一个计数器 RC,当有中央援用该对象时计数器加 1,当援用生效时计数器减 1。通过对象计数器是否为 0 来判断对象是否可被回收。然而相应的,它无奈解决循环援用的问题。
倘若如下:
定义一个字符串
String str = new String ("tony");
其实这个时候,str 援用了字符串 ”tony”,那么这个字符串援用次数就是 1,这时候如果 m 指向了另一个新的值,例如 m=null
,那这个时候 ”tony” 的援用次数就成了 0 了,也就意味着要被回收了。
public class ReferenceCountingGC {
public Object instance;
public ReferenceCountingGC(String name) { }
public static void testGC(){ReferenceCountingGC a = new ReferenceCountingGC("objA");
ReferenceCountingGC b = new ReferenceCountingGC("objB");
// a 和 b 相互援用了
a.instance = b;
b.instance = a;
a = null;
b = null;
}
}
从代码中能够看出,a,b 这两个对象始终在相互援用,如果采纳援用计数法的话,是永远无奈 GC 它们的,因为它们的援用计数永远都不会为 0,所以说援用计数法是有局限性的,或者说它不属于严格意义上的垃圾收集机制。
(1)可达性剖析算法
GC ROOT
是什么?
- 首先咱们晓得标记算法,JVM 的标记算法咱们能够理解为一个可达性算法,所以所有的可达性算法都会有终点,那么这个终点就是 GC Root。也就是须要通过 GC Root 找出所有活的对象,那么剩下所有的没有标记的对象就是须要回收的对象。
- 通过
GC ROOT
的对象作为搜寻起始点,通过援用向下搜寻,所走过的门路称为援用链。通过对象是否有达到援用链的门路来判断对象是否可被回收(可作为 GC ROOT 的对象:虚拟机栈中援用的对象,办法区中类动态属性援用的对象,办法区中常量援用的对象,本地办法栈中 JNI 援用的对象) - 通过可达性算法,胜利解决了援用计数所无奈解决的循环依赖问题,只有你无奈与
GC Root
建设间接或间接的连贯,零碎就会断定你为可回收对象。那这样就引申出了另一个问题,哪些属于GC Root
。 - Java 内存区域中哪些能够作为
GC Root
对象?
虚拟机栈中援用的对象
public class StackLocalParameter {public StackLocalParameter(String name) {}
public static void testGC() {StackLocalParameter s = new StackLocalParameter("localParameter");
s = null;
}
}
办法区中类动态属性援用的对象
public class MethodAreaStaicProperties {
public static MethodAreaStaicProperties m;
public MethodAreaStaicProperties(String name) {}
public static void testGC(){MethodAreaStaicProperties s = new MethodAreaStaicProperties("properties");
s.m = new MethodAreaStaicProperties("parameter");
s = null;
}
}
办法区中常量援用的对象
public class MethodAreaStaicProperties {public static final MethodAreaStaicProperties m = MethodAreaStaicProperties("final");
public MethodAreaStaicProperties(String name) {}
public static void testGC() {MethodAreaStaicProperties s = new MethodAreaStaicProperties("staticProperties");
s = null;
}
}
本地办法栈中援用的对象
任何 native 接口都会应用某种本地办法栈,实现的本地办法接口是应用 C 连贯模型的话,那么它的本地办法栈就是 C 栈。当线程调用 Java 办法时,虚构机会创立一个新的栈帧并压入 Java 栈。然而当它调用的是本地办法时,虚构机会放弃 Java 栈不变,不再在线程的 Java 栈中压入新的帧,虚拟机只是简略地动静连贯并间接调用指定的本地办法。