在 Java 对象被回收之前,首先需要判断该对象是否已经过期或者死亡?常见的判断一个对象是否过期的算法主要有两种,分别为:
在 Java 语言中,可作为 GC Roots 的对象包括以下几种,分别为:
此外,在主流的 Java 虚拟机中大都采用可达性分析算法来判断对象是过期,其中最主要的原因就是引用技术算法无法解决对象之间相互循环引用的问题。例如,在出现下面的情况时,引用计数算法就束手无策了:
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 main(String[] args) {
ReferenceCountingGC objA = new ReferenceCountingGC();
ReferenceCountingGC objB = new ReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;
/**
* 假设在这行发生 GC,objA 和 objB 是否能被回收?
*/
System.gc();
}
}
实际上,无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象的引用链是否可达,判断对象是否存活都与“引用”有关。在 JDK 1.2 之后,Java 对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用(也称为幽灵引用或者幻象引用)4 种,这 4 种引用强度依次逐渐减弱。
Object obj = new Object()
这类的引用,只要强引用还在,垃圾收集器就永远不会回收掉被引用的对象。在虚拟机完成对对象是否存活的判断之后,将正式执行垃圾收集操作。常见的垃圾收集算法,包括:
在 HotSpot 为例,它将 Java 堆划分为新生代和老年代,不同的“年代区域”采用不同的垃圾收集器,例如新生代采用 Serial、ParNew 或者 Parallel Scavenge 收集器;老年代采用 Serial Old、Parallel Old 或者 CMS 收集器;G1 收集器则同时使用于新生代与老年代,即仅使用一个 G1 收集器即可完成对整个 Java 堆的回收工作。其中,Serial 是一个单线程收集器,ParNew 是 Serial 的一个多线程版本收集器;Parallel Scavenge 也是一个多线程且采用复制算法的新生代收集器,与 CMS 等收集器关注点是尽可能地缩短垃圾收集时用户线程的停顿时间不同,Parallel Scavenge 收集器的目标是达到一个可控制的吞吐量;Serial Old 和 Parallel Old 则是 Serial 和 Parallel Scavenge 的老年代版本。
对象的内存分配,往大方向讲,就是在堆上分配(但也可能经过 JIT 编译后被拆散为标量类型并间接地在栈上分配),对象主要分配在新生代的 Eden 区上,如果启动了本地线程分配缓冲(Thread Local Allocation Buffer,缩写为 TLAB),将按线程优先在 TLAB 上分配。少数情况下,也可能会直接分配在老年代中,分配的规则并不是百分之百固定的,其细节取决于当前使用的是哪一种垃圾收集器组合,还有虚拟机中与内存相关的参数的设置。常见的内存分配策略为:
-XX:PretenureSizeThreshold
参数,令大于这个设置值的对象直接在老年代分配。-XX:MaxTenuringThreshold
设置。MaxTenuringThreshold
才能晋升老年代,如果在 Survivor 空间中相同年龄所有对象大小的总和大于 Survivor 空间的一半,年龄大于等于该年龄的对象就可以直接进入老年代。HandlePromotionFailure
设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次 Minor GC,尽管这次 Minor GC 是有风险的;如果小于,或者HandlePromotionFailure
设置不允许冒险,这这时也要改为进行一次 Full GC(指发生在老年代的 GC,会发生 STW)。实际上,在 JDK 6 Update 24 之后的规则变为只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行 Minor GC,否则就进行 Full GC。