为何要了解GC
平时工作中,我们或多或少的都会遇到内存溢出或者内存泄漏的问题,如果我们不了解GC的原理,那么遇到问题时,很多时候会让人不知所措,了解了GC,我们遇到这些问题时,就能通过排查,知道问题的原因,从而解决问题。
GC解决的事情
1、哪些对象需要回收
2、什么时候回收
3、如何回收
GC策略采用什么算法
先让我们回顾下,在上一篇文章中我们已经了解,GC主要发生在java堆和方法区上,也就是全局共享的区域。线程独享(程序计数器、java虚拟机栈、本地方法栈)的区域随着线程而生,随线程而灭,方法结束或者线程结束,内存就跟着回收了,因此这几个区域不必过多的考虑回收问题。
现在我们再来考虑第一个问题,这个问题可以理解为,如何确定堆上面的对象之中,哪些还“活着”,哪些已经“死去”(即不能再被其它途径使用的对象)。
有一种实现简单,效率很高的算法,被称作引用计数算法。它有一个致命的缺陷,就是循环引用的对象无法被回收。下面我们看下示例代码:
public class GCTest {
public Object instance = null;
public static void main(String[] args) {
GCTest objA = new GCTest();
GCTest objB = new GCTest(); //1
objA.instance = objB;
objB.instance = objA; //2
objA = null;
objB = null; //3
System.gc();
}
}
当方法结束,objA和objB都将作为待回收的对象,而如果我们采用引用计数算法,则上述两个对象永远都无法被回收。
大致解释一下,在代码中标注了1、2、3三个数字,当第1个地方的语句执行完以后,两个对象的引用计数全部为1。当第2个地方的语句执行完以后,两个对象的引用计数就全部变成了2。当第3个地方的语句执行完以后,也就是将二者全部归为空值以后,二者的引用计数仍然为1。根据引用计数算法的回收规则,引用计数没有归0的时候是不会被回收的。
可达性分析算法
由于引用计数算法的缺陷,所以在主流的商用jvm一般采用的是可达性分析算法。这种算法的思路是,以“GC Roots”的对象作为起始点,从这些节点开始往下搜索,搜索不到的对象称为不可达对象,将会被判定认为是可回收对象。
就拿上图来说吧,如果采用的是引用计数算法,则object1-7对象均不会被回收,若采用的是可达性分析算法,则object5-7会被回收。
在java语言中,可以作为GC Roots的对象有以下几种:
1、虚拟机栈(栈帧中的本地变量表)中引用的对象。
2、方法区中类静态属性引用的对象。
3、方法区中常量引用的对象。
4、本地方法栈中JNI引用的对象。
垃圾收集算法
可达性分析算法解决的是上面提到的第一个问题,也是最关键的问题,就是哪些对象可以被回收。
不过GC还要解决后面两个问题,在可达性分析算法的基础上,在现代虚拟机的实现中,垃圾收集算法主要有下面三种:
标记-清除算法、复制算法、标记-整理算法,关于这三种算法将在下一篇跟大家一起讨论,也比较好理解。