GC(垃圾回收)最主要的作用是回收程序中不再使用的内存。当使用new创建对象后, Java虚拟机会给其分配一块内存资源, 当对象无用的时候Java的垃圾回收器负责回收无用对象占用的内存资源。如果你的对象并非用new获取了一块“特殊”内存,垃圾回收只知道释放那些由New分配的内存, 它并不知道如何释放对象的“特殊内存”,为了应对这种情况,Java允许在类中命名finalize方法
GC垃圾回收过处理可达性分析算法中不可达的对象被标记成需要回收, 一但垃圾回收器准备释放对象占用的内存会经理两个过程
- finalize方法没有被虚拟机调用过,那么先调用对象的finalize方法。本次不再处理等待下一次垃圾回收处理
- finalize方法调用过一次后,再次垃圾回收释放对象占用的内存
第一个过程中, 如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会放置在一个叫做F-Queue的队列之中,并在稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程去执行它。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束,这样做的原因是,如果一个对象在finalize()方法中执行缓慢,或者发生了死循环(更极端的情况),将很可能会导致F-Queue队列中其他对象永久处于等待,甚至导致整个内存回收系统崩溃。finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那基本上它就真的被回收了
public class FinalizeEscapeGC {
public static FinalizeEscapeGC SAVE_HOOK = null;
public void isAlive() {
System.out.println("yes,i am still alive:)");
}
@
Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize mehtod executed!");
FinalizeEscapeGC.SAVE_HOOK = this;
}
public static void main(String[] args) throws Throwable {
SAVE_HOOK = new FinalizeEscapeGC();
//对象第一次成功拯救自己
SAVE_HOOK = null;
System.gc();
//因为finalize方法优先级很低,所以暂停0.5秒以等待它
System.out.println("sleep 500");
Thread.sleep(500);
System.out.println("after sleep 500");
if (SAVE_HOOK != null) {
SAVE_HOOK.isAlive();
} else {
System.out.println("no,i am dead:(");
}
//下面这段代码与上面的完全相同,但是这次自救却失败了
SAVE_HOOK = null;
System.gc();
//因为finalize方法优先级很低,所以暂停0.5秒以等待它
Thread.sleep(500);
if (SAVE_HOOK != null) {
SAVE_HOOK.isAlive();
} else {
System.out.println("no,i am dead:(");
}
}
}
从上面用例看出,第一次垃圾回收候调用finalize方法并没有被回收, 第二次回收才被回收。
虽然finalize()方法可以在被对象销毁的时候调用,但是不建议把清理对象某些行为的在finalize()方法中执行, 因为这个方法的触发条件是GC操作,如果没有GC操作这个操作就不会被调用。《Java编程思想》里提到过一个例子: 某个对象将图绘制在屏幕上,在finalize()方法中增加了擦除功能,目的是当垃圾回收时执行finallize()方法的时候擦除图形,但是这个操作必须要垃圾回收才会执行,如果垃圾回收没有发生,那么图像就会一直保存在内存里。因此不能指望finallize, 更好的做法是在对象中明确擦除的行为, 当需要擦除的时候明确调用该行为