细说Java GC

GC是什么?Java初学者可能很少听到这个名词,但它的中文肯定听过:垃圾回收,英文全称为:Garbage Collection。

看过上一篇《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回收的对象。

细说Java GC_第1张图片


上面的内容,都涉及到一个名词:引用。那什么是引用?

通俗的解释为:一个reference类型的数据中存储的数值代表另外一块内存的起始地址,这块内存代表的就是一个引用。
其实,Java1.2以后,引用不止这么简单,有四种引用,分别为:强引用(如Object obj = new Object()),软引用、弱引用、虚引用。这里,我就抛砖引玉一下,大家想了解的可以Google或者度娘一下。(PS:因为我本人也不是很了解,呵呵)。

GC对方法区的回收是怎样判断的呢?

GC对于堆的回收,效率是比较高的,一次回收,基本上都可以回收百分之七八十的空间。而方法区,效率要低得多。
对于此部分,GC主要回收两部分:废弃常量和无用类。

废弃常量:如一个字符串“ABC”进入了常量池,但是系统中,没有一个String对象叫做“ABC”,即没有任何String对象引用它,那么,这个字符串就很可能被GC回收。常量池中的接口、方法、字段等都类似。

无用类:必须满足以下三个条件,才可能被GC回收
* 该类所有实例都已经被回收,即堆中不存在该类实例
* 加载该类的ClassLoader已经被回收
* 该类对应的java.lang.Class对象没有被任何地方引用,无法通过反射机制访问该类

你可能感兴趣的:(java)