我在某个技术群里发现很多人对GC的问题是最多的。确实,由于Java的GC经常会刷存在感(例如占用大量的CPU时间,full gc时直接失去响应),GC的问题就成了初级的Java程序员向中级程序员进步过程中不得不面对的一个问题。但是到目前为止,我还没有在网上看到一个系统的全面的针对Java GC进行总结的文章。我希望在我的公众号里专门针对Java GC做一下梳理。
这一期,我们先从Java的分代式GC讲起。
垃圾回收(Garbage Collection,GC),本质上是一种自动内存管理技术。我们知道在C或者C++语言中,内存的申请和释放是要通过程序员手动操作的,而在Java中,则不必这样做。这是因为Java语言可以自动GC。GC的意思是把不用的内存回收掉。
把分配到堆中那些不能通过程序引用的对象称为非活动对象,也就是死掉的对象,我们称为“垃圾”。因为我们期望让内存管理变得自动,我们就必须做两件事情: 1. 找到内存空间里的垃圾;2. 回收垃圾,让程序员能再次利用这部分空间 。只要满足这两项功能的程序,就是GC。不一定非得说是在某种语言的runtime里实现的才叫GC。(这句话的意思是,你完全可以自己为C++写一个内存管理模块,以第三方库的形式接管C++的内存申请和释放,这也是GC)
Java程序中的各个object之间的引用关系是一个带有明显方向性的关系。例如下面的例子:
public class Main {
public static void main(String args[]){
try {
A a = new A();
a.b = new B();
a.c = new C();
a.b.a = a;
a.b.c = a.c;
} catch (Exception e) {
e.printStackTrace();
}
}
}
class A {
public B b;
public C c;
}
class B {
public A a;
public C c;
}
class C {
Runnable r;
}
我们创建了三个对象,如果某一个对象 x 引用了另一个对象 y,那我们就通过一条有向边把这两个对象的引用关系表示出来,那么上述程序就可以表示成:
大家可以看到,我们经过这样的抽象以后,就把对象之间的引用关系转换成了一个有向图。如果我们在原来的逻辑上,再增加一行语句:
a.refB = new B();
这时,从 a 到 b 的引用就不存在了,而是转向了另外一个 B 的实例。那么这个 b 就不再被任何对象引用了,就可以标记为垃圾了(实际上,在这个例子中还是有一些细微的差别的,b并不会直接变成垃圾,这是因为从当前程序栈上还有一个指向b对象的隐含的引用。我们现在不必在意这些细节,以后会慢慢细化)
我们已经把对象之间的引用关系转换成了一个有向图,那么我们就可以使用图算法来处理内存管理领域的一些问题了。