Java中finalize()详解和Java9中的垃圾回收

Java垃圾回收机制



Java有垃圾回收期负责回收无用对象占据的内存空间。但也有特殊情况:假定你的对象(并非使用new)获得了一块“特殊”的内存区域,由于垃圾回收期只知道释放那些经由new分配的内存,所以它不知道该如何释放该对象的这块“特殊”内存。
Java允许在类中定义一个名为finalize()方法。一旦垃圾回收期准备好释放对象占用的内存空间,首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。
在Java中,对象并非总是被垃圾回收,只要程序没有濒临内存空间用完的那一刻,对象占用的空间就中也得不到释放。如果程序执行结束,并且垃圾回收器一直没有释放创建的对象,程序结束退出后资源也会全部返回给操作系统。
这个垃圾回收策略避免了频繁垃圾回收造成的开销,Java虚拟机并未面临内存耗尽的情形,它是不会浪费时间去执行垃圾回收以恢复内存的

finalize()用途



不论对象是如何创建的,垃圾回收器都会负责释放对象的所有内存。
finalize()需求限制到一种特殊情况:通过某种创建对象方式以外的方式为对象分配了存储空间。
需要使用finalize()的原因,是由于在分配内存时可能采用了C语言中的做法,而非Java的通常做法。主要发生在使用本地方法的情况下。
在非Java代码中,也许会调用C语言的malloc()函数系列来分配存储空间,而且除非用了free()函数,存储空间将得不到释放,从而造成内存泄漏。这时需要在finalize中用本地方法调用它。
不要过多使用finalize(),它不是进行普通清理工作的方法。避免使用finalize()函数,finalize()函数无法预料,常常是危险的,总之是多余的。
finalize()用法示例:对象终结条件的验证


在对象不在被引用时,finalize()方法会被垃圾收集器调用。
通常不能指望finalize(),必须创建其他的“清理”方法,并且明确的调用它们。
这里有一个应用场景:终结条件的验证。
当对某个对象不再感兴趣了,对象就可以被清理了。这个对象应该处于某种状态,使它占用的内存可以被安全地释放。只要对象中存在没有被适当清理的部分,程序就存在很隐晦的缺陷。finalize()可以用来发现最终发现这种情况,尽管它并不总会被调用。如,打开的文件,在垃圾回收前程序员应该关闭这个文件等。

以下示例改编自《Java编程思想》

//: initialization/TerminationCondition.java
// Using finalize() to detect an object that
// hasn't been properly cleaned up.

class Book {
  boolean checkedOut = false;
  Book(boolean checkOut) {
    checkedOut = checkOut;
  }
  void checkIn() {
    checkedOut = false;
  }
  @Override
  protected void finalize() {
	  try {
    if(checkedOut) {
    	System.out.println("Error: checked out");
    	}
	  }finally {
		  try {
			super.finalize();
		} catch (Throwable e) {
			e.printStackTrace();
		}
	  }
  }
}

public class TerminationCondition {
  public static void main(String[] args) {
    Book novel = new Book(true);
    // Proper cleanup:
    novel.checkIn();
    // Drop the reference, forget to clean up:
    new Book(true);
    // Force garbage collection & finalization:
    System.gc();
  }
} /* Output:
Error: checked out
*///:~

本例的终结条件是:所有的Book对象在被当做垃圾回收前应该处于checkOut==false的状态。这里可以通过finalize()来验证终结条件,发现程序设计中的缺陷。
System.gc()用于强制进行终结动作。 如果finalize方法抛出未捕获的异常,该异常被忽略,并终止对象的回收。

Java9中的垃圾回收机制



finalize机制本身就是存在问题的。
finalize机制可能会导致性能问题,死锁和线程挂起。
finalize中的错误可能导致内存泄漏;如果不在需要时,也没有办法取消垃圾回收;并且没有指定不同执行finalize对象的执行顺序。此外,没有办法保证finlize的执行时间。
遇到这些情况,对象调用finalize方法只有被无限期延后。

Java9中finalize方法已经被废弃。

占有非堆资源的对象实例,类应该提供一个方法以明确释放这些资源,如果合适的话他们也应该实现AutoCloseable接口。

java.lang.ref.Cleaner和java.lang.ref.PhantomReference提供更灵活和有效的方式,在对象无法再访问时释放资源。

Cleaner示例

import java.lang.ref.Cleaner;

public class CleaningExample implements AutoCloseable {
	// A cleaner, preferably one shared within a library
	private static final Cleaner cleaner = Cleaner.create();

	static class State implements Runnable {

		State() {
			System.out.println("init");// initialize State needed for cleaning action
		}

		public void run() {
			System.out.println("clean");// cleanup action accessing State, executed at most once
		}
	}

	private final State state;
	private final Cleaner.Cleanable cleanable;

	public CleaningExample() {
		this.state = new State();
		this.cleanable = cleaner.register(this, state);
	}

	public void close() {
		cleanable.clean();
	}
	
	public static void main(String[] args) {
		while(true) {
			new CleaningExample();
		}
	}
}

本例中每次创建对象时,都会打印init;回收对象时,都会打印clean。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值