对象是否存活
1. 引用计数算法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。但是主流的Java虚拟机里面没有选用引用计数算法来管理内存,其中最主要的原因是它很难解决对象之前相互循环引用的问题。如下代码:
虚拟机运行参数 -XX:+PrintGC -XX:+PrintGCDetails
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;
System.gc();
}
0.319: [GC (System.gc()) [PSYoungGen: 9349K->5259K(76288K)] 9349K->5267K(251392K), 0.0026774 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.322: [Full GC (System.gc()) [PSYoungGen: 5259K->0K(76288K)] [ParOldGen: 8K->5100K(175104K)] 5267K->5100K(251392K), [Metaspace: 3462K->3462K(1056768K)], 0.0050525 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
通过打印GC日志,可以发现:PSYoungGen: 9349K->5227K,意味着虚拟机并没有因为这连个对象互相引用就不回收他们,也反映出虚拟机并不是通过引用计数算法来判断对象是否存活的。
2. 可达性算法
它是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路劲称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。如下图所示:
在Java语言中,可作为GC Roots的对象包括下面几种:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(即一般说的Native方法)引用的对象
3. 再谈引用
○强引用:是指在程序代码中普遍存在的,类似于“Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
○软引用:是用来描述一些有用但并非必要的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。
这里举一个软引用的例子
/*-Xms10m -Xmx10m*/
static class HeapObject {
byte[] bs = new byte[1024 * 1024];
}
//软引用
public static void main(String[] args) {
SoftReference<HeapObject> softReference = new SoftReference<>(new HeapObject());
List<HeapObject> list = new ArrayList<>();
while (true) {
if (softReference.get() != null) {
list.add(new HeapObject());
System.out.println("list.add");
} else {
System.out.println("---------软引用已被回收---------");
break;
}
System.gc();
}
}
----------------------------------------------------------------------------------------------------------------------------
可以看到软引用的对象确实是被回收了。
○弱引用:是用来描述非必须要的对象,但它的强度逼软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会被回收掉。
○虚引用:它是最弱的一种引用关系,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获取一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被垃圾收集器回收时收到一个系统通知。
4.回收方法区
在方法区中进行垃圾收集的“性价比”一般比较低;在堆中,尤其是在新生代中,常规应用进行一次垃圾收集一般可以回收70~95%的空间,而永久代的垃圾收集效率远低于此。
永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。以常量池中字面量的回收为例,假如一个字符串“abc”已经进入了常量池中,但是当前系统没有任何一个String对象是叫做“abc”的,换句话说就是没有任何String对象引用常量池中的“abc”常量,如果发生内存回收而且必要的话,这个常量会被系统清理出常量池。常量池中的其他类(接口)、方法、字段的符号引用也于此类似。
无用的类:
①该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例
②加载该类的ClassLoader已经被回收
③该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法