GC是什么?Java初学者可能很少听到这个名词,但它的中文肯定听过:垃圾回收,英文全称为:Garbage Collection。
看过上一篇《Java虚拟机内存管理》的朋友想一下,GC的主要对象是哪几个内存区域呢?
上一篇提到,Java内存管理为5个区域,分别是程序计数器、虚拟机栈、本地方法栈、堆、方法区。其中,程序计数器、虚拟机栈中的栈帧、本地方法栈是线程独有,独立使用一块内存的。故而,它们的生命周期是和线程同步的,正所谓一定同年同月同日生,必须同年同月同日死的好兄弟啊。但是,堆和方法区,是所有线程共享使用,每个线程拥有的内存大小,都是动态的,对于它们的内存回收也是不确定的。故,GC主要是针对它们而生的。
如何判断对象是否已死?
有一个比较广泛的算法:引用计数算法。该算法描述为:给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻,计数器值为0的对象,就是不可能被引用的。
像微软的COM、使用ActionScript3的FlashPlayer、Python等都是用这个算法。但是,Java是使用这个算法吗?
它的运行结果为:
[ GC 4408K->144K(59648K), 0.0011230 secs]
[Full GC 144K->116K(59648K), 0.0053720 secs]
注意第一句 GC 4408K->144K,这说明,Java的GC,将对象回收了的。由此,可以说明,Java的GC机制,不是使用引用计数算法的。
那,JavaGC使用什么算法呢?
首先介绍一下,这个算法:根搜索算法,通过一系列名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连(就是这个对象到GC Roots节点不可到达)时,则证明这个对象是不可到达的,就会被GC回收。
如下所示,其中Object7,Object8,Object9,就是被不可到达GC Roots的,就是GC回收的对象。
看过上一篇《Java虚拟机内存管理》的朋友想一下,GC的主要对象是哪几个内存区域呢?
上一篇提到,Java内存管理为5个区域,分别是程序计数器、虚拟机栈、本地方法栈、堆、方法区。其中,程序计数器、虚拟机栈中的栈帧、本地方法栈是线程独有,独立使用一块内存的。故而,它们的生命周期是和线程同步的,正所谓一定同年同月同日生,必须同年同月同日死的好兄弟啊。但是,堆和方法区,是所有线程共享使用,每个线程拥有的内存大小,都是动态的,对于它们的内存回收也是不确定的。故,GC主要是针对它们而生的。
如何判断对象是否已死?
有一个比较广泛的算法:引用计数算法。该算法描述为:给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻,计数器值为0的对象,就是不可能被引用的。
像微软的COM、使用ActionScript3的FlashPlayer、Python等都是用这个算法。但是,Java是使用这个算法吗?
用如下这段代码,可以说明
/**
* 运行这段程序,需要在运行设置的VM Arguments中写入如下语句
* -verbose:gc
* 它的作用是在控制台输出GC的详细信息。
* @author qianl
*
*/
public class Test {
public Object instance = null;
private static final int _1M = 1024 * 1024;
private byte[] size = new byte[2 * _1M]; //占用一点内存,以便在GC日志中观察是否被回收过
public static void main(String[] args) {
// TODO Auto-generated method stub
Test t1 = new Test();
Test t2 = new Test();
t1.instance = t2; //t2对象被引用,如果Java使用引用计数算法,那么其值+1
t2.instance = t1; //t1对象被引用,如果Java使用引用计数算法,那么其值+1
t1 = null; //t1为null,它的成员变量instance所引用的对象将不会被用到。
t2 = null; //t2为null,它的成员变量instance所引用的对象将不会被用到。
/**
* 在此使用Java的垃圾回收,由上面代码可知,t1和t2两个对象将不可能被用到,但是他们的引用计数的值
* 却不为0,它们会被回收吗?
*/
System.gc();
}
}
它的运行结果为:
[ GC 4408K->144K(59648K), 0.0011230 secs]
[Full GC 144K->116K(59648K), 0.0053720 secs]
注意第一句 GC 4408K->144K,这说明,Java的GC,将对象回收了的。由此,可以说明,Java的GC机制,不是使用引用计数算法的。
那,JavaGC使用什么算法呢?
首先介绍一下,这个算法:根搜索算法,通过一系列名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连(就是这个对象到GC Roots节点不可到达)时,则证明这个对象是不可到达的,就会被GC回收。
如下所示,其中Object7,Object8,Object9,就是被不可到达GC Roots的,就是GC回收的对象。
上面的内容,都涉及到一个名词:引用。那什么是引用?
通俗的解释为:一个reference类型的数据中存储的数值代表另外一块内存的起始地址,这块内存代表的就是一个引用。
其实,Java1.2以后,引用不止这么简单,有四种引用,分别为:强引用(如Object obj = new Object()),软引用、弱引用、虚引用。这里,我就抛砖引玉一下,大家想了解的可以Google或者度娘一下。(PS:因为我本人也不是很了解,呵呵)。
GC对方法区的回收是怎样判断的呢?
GC对于堆的回收,效率是比较高的,一次回收,基本上都可以回收百分之七八十的空间。而方法区,效率要低得多。
对于此部分,GC主要回收两部分:废弃常量和无用类。
废弃常量:如一个字符串“ABC”进入了常量池,但是系统中,没有一个String对象叫做“ABC”,即没有任何String对象引用它,那么,这个字符串就很可能被GC回收。常量池中的接口、方法、字段等都类似。
无用类:必须满足以下三个条件,才可能被GC回收
* 该类所有实例都已经被回收,即堆中不存在该类实例
* 加载该类的ClassLoader已经被回收
* 该类对应的java.lang.Class对象没有被任何地方引用,无法通过反射机制访问该类