终结 finalize()和垃圾回收(garbage collection)

时间:2022-05-04
本文章向大家介绍终结 finalize()和垃圾回收(garbage collection),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

1、为什么要有finalize()方法?

    假定你的对象(并非使用new)获得了一块“特殊”的内存区域,由于垃圾回收器只知道释放那些经由new分配的内存,所以他不知道该如何释放该对象的这块“特殊”内存,为了应对这种情况,java 允许在类中定义一个finalize()的方法。

    protected void finalize(){
        
    }

2、finalize()方法在何时调用?

    一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。

3、finalize()的局限性?

  •     对象可能不被回收。
  •     垃圾回收并不等于“析构”。
  •     垃圾回收值与内存有关。

    对于与垃圾回收有关的任何行为来说(尤其是finalize()方法),他们也必须与内存及其回收有关,所以在finalize()方法内做普通的清除工作是不合适的。

    那么在finalize()中释放对象是否正确呢?不!如果JVM 并未面临内存耗尽的情形,它是不会浪费时间在回收垃圾上的。即使,JVM进行了垃圾回收, 也无法确切地保证垃圾回收器何时调用该方法,也无法保证调用不同对象的方法的顺序。即使一个对象包含另一个对象的引用,或者在释放一个对象很久以前就释放了另一个对象,也可能会以任意的顺序调用这两个对象的Finalize方法。如果必须保证采用特定的顺序,则必须提供自己的特有清理方法。

4、finalize()用途?

    既然finalize()这么废,好像什么都不了,那么到底用它来干嘛呢?

    当“使用本地方法”分配内存,例如使用malloc()分配存储空间,除非调用free()否则内存空间将得不到

释放,从而内存泄露。因此free方法就应该在finalize()中用本地方法调用它。

     要是对象代表一个打开的文件,在对象被回收前程序员应该关闭这个文件。再要对象中存在没有被适当清除的部分,你的程序就存在很隐晦的错误,finalize()的价值就是用来最终发现这种情况。-------《Thinking in Java》

     个人认为,finalize()还是太不可靠,平常还是少用的好。

      如果一定要进行回收动作,最好自己写一个回收方法dispose()方法。应当注意的是如果子类重写了父类的dispose()方法,当进行清除动作时,应该先清除子类的,再清除父类的,原因在于:可能子类存在对父类的方法调用。

5、垃圾回收器如何工作?

    在某些Java虚拟机中,堆的实现方式就像一个传送带,你每分配一个新对象,它就往前移动一格,这意味着对象存储空间的分配速度非常快。但是,堆的工作又不完全像传送带一样,想象一下,要是堆指针不断向前,势必会导致频繁的内存调度,并最终耗尽资源。其中的秘密在于垃圾回收器的介入。当它工作时,将一面回收空间,一面使堆中的对象紧凑排列,这样“堆指针”就可以很容易移动到更靠近传送带的开始处,也就尽量避免了页面错误。

    垃圾回收器模式之引用计数(reference counting):是一种简单但速度很慢的垃圾回收技术。每个对象都含有一个引用计数器,当有引用连接至对象时,引用计数加1。当引用离开作用域或被置为null时,引用计数器减1。垃圾回收器会在含有全部对象的列表上,当发现某个对象的引用技术为0时,就释放其占有的空间。这种模式有种缺陷,如果对象之间存在循环引用,可能会出现“对象应该被回收,但引用计数却不为0的情况”

    垃圾回收器模式之停止-复制(stop-and-copy):这种垃圾回收基于一种更快的寻找“活”的对象的方法,并能有效爱的解决对象之间的循环利用。这种方式是:遍历所有对象的引用,这个引用可能会穿过数个对象层次,并最终追溯到其存活在堆栈或静态存储区之中的引用,如果对象之间存在循环利用的话,遍历追溯到最后会发现寻找的是本身的对象,因此这些对象根本不会被发现。而stop-and-copy方式意味着先暂停程序的运行,然后将所有存活的对象从当前堆复制到另外一个堆,没有被复制的全部都是垃圾,当对象被复制到新堆时,他们是一个挨着一个的。所以新堆保持紧凑排列。值得注意的是,大型对象占有自己的内存块,copy时不进行复制,如果该对象被引用就把该内存块的代数加1来表示被引用到了。

    垃圾回收器模式之标记-清扫(mark-and-sweep): stop-and-copy存在两个问题:一、你得有两个堆,然后你得在这两个分离的堆之间来回倒腾,从而维护比实际需要多一倍的空间。某些Java虚拟机对此问题的处理方式是,按需从堆中分配几块较大的内存,复制动作发生在这些大块内存之间。二、如果稳定的程序只产生少量的垃圾,复制动作岂不是很浪费?为此引出了mark-and-sweep模式,当它遍历所有的引用,进而找出所有存活的对象,每当它找到一个存活对象,就会给对象一个标记,这个过程不会回收任何对象。只有在标记工作完成后,没被标记的对象被垃圾回收器回收。剩下的空间的不连续的,垃圾回收器要是希望得到连续空间的话,就得重新整理剩下的对象。对一般用途而言,mark-and-sweep 方式速度相当慢,但是用在处理少量垃圾或不产生垃圾时,它的速度就很快了。mark-and-sweep 工作也必须在程序暂停的情况下工作。

    垃圾回收器采用的 自适应 的技术,按照需求在各个模式之间切换,当JVM监视到所有对象都很稳定,只产生了少量的垃圾,就自动采用mark-and-sweep模式,要是堆空间出现很多碎片,就会切换回 stop-and-copy方式。