美文网首页
Java清理:finalize

Java清理:finalize

作者: 都让你们叫老了 | 来源:发表于2017-11-27 15:54 被阅读0次

    简述:

    相信每个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()中看到的那样。在本例中,它被注释掉了,因为他需要进行异常处理。

    相关文章

      网友评论

          本文标题:Java清理:finalize

          本文链接:https://www.haomeiwen.com/subject/mbjdbxtx.html