java学习笔记之对象清除、垃圾收集

本篇来聊聊java中内存回收机制。

如果有c++编程经验的朋友,肯定知道在c++中如果创建了一个对象,当不再使用的时候,需要手动调用delete方法来进行销毁。那么在java中我们为什么可以自由的创建对象而不用去考虑(其实也是要考虑的,只是平常忽略了它)?因为java提供了一套完整的垃圾回收机制(gc)。它会自动扫描内存中我们所创建过且不再使用的对象,进行自动销毁。听上去是不是很高端呢~

虽然听上去很不错并且它也确实能帮助我们自动销毁不用的对象,但它并非完美,因为我们在程序运行的时候,我们并不知道gc什么时候会来销毁那些我们所不再使用的对象,gc一般只有在资源开始紧张的时候,才会以独立的优先级的方式运行。事实上如果说gc足够完美的话,所谓的内存溢出是不是就很少出现了呢?然而现实中还是会出现内存溢出的情况的,这也从另一方面说明了gc的工作流程是不能被保证的。

下面我们要说说几个相关的方法,finalize(),System.gc(),System.runFinalization(),System.runFinalizersOnExit(true)。

finalize():
finalize方法是定义在Object对象中的:

protected void finalize() throws Throwable { }

该方法只有在gc开始着手清理该对象时,才会执行该方法,然而jvm并不是必定会执行该方法的,比如你的程序在退出前都没有释放过该资源,那么该方法就不会执行。所以如果你重写了该方法,并在其中写了一定的操作,那么它是不能被保证一定执行的。

 

System.gc():
该方法有时被人称作强制调用系统的gc,然而这是不准确的,该方法只是建议gc进行一次回收,然而至于它是否执行,这是不受保障的。

System.runFinalization():
该方法是用来强制调用已经失去引用的对象finalize方法。

System.runFinalizersOnExit(boolean value):
该方法用于在程序结束后进行垃圾回收。该方法已被废弃,因为其存在安全性问题,因为它可能导致仍在活动的对象调用finalizers,当有线程并发操作这些对象的时候就会出现一些奇怪的行为和死锁。

下面通过一个小例子来加深理解:

package test;


public class GCTest {
	public static void main(String[] args) {
		//当运行时没传入参数提醒传入参数,before/after/others
		if (args.length == 0) {
			System.err.println("Usage: \n" + "java Garbage before\n or:\n"
					+ "java Garbage after");
			return;
		}
		
		//当finishCreate标记不为true时,持续创建Demo对象和String对象,String对象是为了加大内存消耗,来吸引gc的关注
		while (!Demo.finishCreate) {
				new Demo();
				new String("To take up space");
				
		}
		System.out.println("After all Demos have been created:\n"
				+ "total created = " + Demo.createdNum + ", total finalized = "
				+ Demo.finalizedNum);
		
		//如果传入的参数为before,则先后执行System.gc()和System.runFinalization();
		if (args[0].equals("before")) {
			System.out.println("gc():");
			System.gc();
			System.out.println("runFinalization():");
			System.runFinalization();
		}
		System.out.println("bye!");
		//如果传入的参数为after,则调用System.runFinalizersOnExit(true),该方法已被废弃,因为存在安全性
		if (args[0].equals("after"))
			System.runFinalizersOnExit(true);
	}
}


class Demo {
	// 判断当前内中gc是否运行
	static boolean gcrun = false;
	// 用于控制是否继续生成对象的开关
	static boolean finishCreate = false;
	// 用来记录对象创建的数量
	static int createdNum;
	// 用来记录对象被销毁的数量
	static int finalizedNum;
	
	static boolean tempFlag;
	// 用来区分当前对象的标记
	int id;


	// 无参构造函数
	public Demo() {
		// 每创建一个对象,createNum+1,并将其赋值给当前对象id
		id = ++createdNum;
		if (createdNum == 50) {
			System.out.println("the fiftieth Demo has been created");
		}
	}


	@Override
	protected void finalize() throws Throwable {
		//当第一次执行finalize的时候,表示gc开始自动销毁对象了
		if (!gcrun) {
			gcrun = true;
			System.out.println("Gabage Collection begins running after "
					+ createdNum + "demo objects have been created");
		}
		//当销毁的对象id>=50时,将finishCreate标记置为true,停止创建更多的对象
		if (id >= 50 && !tempFlag) {
			tempFlag = true;
			finishCreate = true;
			System.out
					.println("the fiftieth demo begins to finalize and stop creating more demos");
		}
		//没执行一次finalize方法,finalizeNum都加1
		finalizedNum++;
//		System.out.println(finalizedNum+"===="+createdNum);
		//当finalizeNum >= createNum,表示所有对象都被清除
		if (finalizedNum >= createdNum) {
			System.out.println("all gabage have benn collected");
		}
	}


}


运行上述代码,传入不同的参数,可以进行不同的操作,Eclipse中带参的运行方法为右键Run As > Run Configurations... > Arguments 填入参数:

 

下面为几种运行结果:

1.传入参数before运行:


打开Demo类finalize中的打印语句可以看到如下打印:


第一种执行的是System.gc()和System.runFinalization()方法,从最后的输出可以看出,并没有完全的销毁对象,程序就结束了,这再一次证明了java垃圾回收机制的不确定性。

 

2.传入参数after运行:

打开Demo类finalize中的打印语句可以看到如下打印:

通过以上打印可以看出,所有的对象都被清理了,但明显感觉到程序的效率变慢了。

3.传入参数test运行:


打开Demo类finalize中的打印语句可以看到如下打印:

传入test,表示任由java的垃圾回收机制运行,不做人为干涉,可以看出,其活动性比较不频繁,因为此时内存还比较充足。

一般情况下,我们并不需要自己去提醒gc进行内存回收,当我们使用完一个对象后,推荐是将它置为null,由gc自动回收。

以上为本篇内容。

 

posted @ 2017-08-30 10:31  moonfish  阅读(2214)  评论(0编辑  收藏  举报