Java清理:finalize

简述:

相信每个C程序员都知道销毁对象的重要性,也熟悉析构函数,但是java程序员可能就不太会关注销毁对象。因为java有垃圾回收器负责回收无用对象占据的内存资源。当然也有特殊情况:假定你的对象(并非使用new)获得了一块“特殊”的内存区域,由于垃圾回收器只知道释放那些由new分配的内存,所以它不知道该如何释放这块“特殊内存”。

为了应对这个特殊的情况,java允许在类中定义一个名为finalize()的方法。它的工作原理“假定”是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法。并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。所以要是你打算用finalize(),就能在垃圾回收时刻做一些重要的清理工作。

这里有一潜在的编程陷阱,因为有些程序员(特别是C++程序员)刚开始可能会把finalize()当做C++中的析构函数。所有有必要明确区分一下。在C++中如果程序没有缺陷的话,那么对象一定会被销毁。而java的对象却并非总是被垃圾回收,或者换句话说:

1.对象可能不被垃圾回收。
2.垃圾回收并不等于“析构”。

因为垃圾回收本身也会有开销,如果不使用它,那么就不会支付这部分开销。所以当程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得不到释放。如果程序执行结束,并且垃圾回收器一直都没有释放你创建的任何对象的存储空间,则随着程序的退出,那些资源也会全部交还给操作系统。所以这里的第三点:

3.垃圾回收只与内存有关

finalize()的用途何在:

使用垃圾回收器的唯一原因是为了回收程序不再使用的内存。所以对于与垃圾回收有关的任何行为来说(尤其是finalize方法),它们也必须同内存及其回收有关。
但是并不意味之对象中含有其他对象,finalize()就应该明确释放那些对象。无论对象是如何创建,垃圾回收器都会负责释放对象占据的所有内存。这就将对finalize()的需求先知道一种特殊情况,即通过某种创建对象方式为对象分配了存储空间。而这种方式非java中通常的做法。这种情况主要发生在使用“本地方法”的情况下,本地方法是一种在java中调用非java代码的方式。本地方法目前只支持C和C++,但他们可以调用其他语言写的代码,所以实际上可以调用任何代码。在非java代码中,也许会调用C的malloc()函数系列来分配存储空间,而且出分调用了free()函数,否则存储空间将得不到释放,从而造成内存泄漏。当然,free()是C和C++中的函数,所以需要在finalize()中用本地方法调用它。所以要有finalize()。

必须要实施清理

要清理一个对象,用户必须需要清理的时刻调用执行清理动作的方法。这听起来似乎很简单。在C++中创建了一个局部对象(也就是在堆栈中,这在java中是行不通的),应该被销毁,但是程序员忘记调用delete()函数,那么永远不会调用析构函数,这样就会出现内存泄露,对象的其他部分也不会得到清理。这种缺陷很难跟踪。
相反,java不允许创建局部对象, 必须使用new创建对象。在java中,也没有用于释放对象的delete,因为垃圾回收器会帮助你释放存储空间。甚至可以肤浅的认为,正是由于垃圾收集机制的存在,使得java没有析构函数。然而,垃圾回收器的存在并不能完全代替析构函数。(而且绝对不能直接调用finalize(),因为finalize方法是Object类的一个Protected方法。Protected方法只能被子类内部调用,外部不能直接调用 ,所以这也不是一种解决方案。)如果希望进行除释放存储空间之外的清理工作,还是得明确调用某个恰当的方法。这就等同于使用析构函数了,只是乜有它方便。
无论是“垃圾回收”还是“终结”,都不保证一定会发生。如果java虚拟机并未面临内存消耗的情形,他是不会浪费时间去执行垃圾回收以恢复内存的。

class Book {
    boolean checkedOut = false;

    public Book(boolean checkOut) {
        // TODO Auto-generated constructor stub
        checkedOut = checkOut;
    }

    void checkIn() {
        checkedOut = false;
    }

    protected void finalize() {
        if (checkedOut) {
            System.out.println("Error: checked out");
        }
        //Nomally,you'll also do this。
        //super.finalize();
    }
}

public class TestFinalize {
    public static void main(String[] args) {
        Book novel = new Book(true);
        novel.checkIn();
        new Book(true);
        System.gc();
    }
}

本例的终结条件是:所有Book对象再被当做垃圾回收钱都应该被签入(check in)。但是mian()方法中,由于程序员的错误,有一本书未被签入。如果没有finalize()来验证终结条件,将很难发现这种缺陷。
注意,System.gc()用于强制进行终结动作。即使不这么做,通过重复地执行程序(假设程序将分配大量的存储空间而导致垃圾回收动作的执行),最终也能找出错误的Book对象。
你应该总是假设基类版本的finalize()也要做某些重要的事情,因此要使用super来调用它,就像在Book.finalize()中看到的那样。在本例中,它被注释掉了,因为他需要进行异常处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值