终结 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方式。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大闲人柴毛毛

深入了解JVM垃圾收集器

程序计数器、JVM栈、本地方法栈这三个内存区域和线程是一一对应的,并且每一个线程的这三个区域相互独立互不干扰。他们都随着线程的产生而产生,线程的灭亡而灭亡。JV...

2896
来自专栏Java技术栈

Java对象引用四个级别(强、软、弱、虚)

最近,高级Java技术栈微信群中,有一些猿友在讨论JVM中对象的周期问题,有谈到引用的级别,现在为大家做个总结吧,虽然大多数公司并没有意识或者用到这些引用,但了...

43913
来自专栏武军超python专栏

2018年8月26日python中的垃圾回收机制(Garbage Collection:GC)

垃圾回收机制(Garbage Collection:GC)基本是所有高级语言的标准配置之一了

2534
来自专栏互联网大杂烩

Java垃圾回收机制

在Java中,当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾。JVM的一个系统级线程会自动释放该内存块。垃圾回收意味着程序不再需要的对象是"无用...

3025
来自专栏猿人谷

总结---5

1.语义搜索 所谓语义搜索,是指搜索引擎的工作不再拘泥于用户所输入的关键字,而是准确捕捉到用户所输入语句后面的真正意图,并以此来进行搜索,微软、谷歌和Fac...

21410
来自专栏用户2442861的专栏

全面分析Java的垃圾回收机制

【简 介】 Java的堆是一个运行时数据区,类的实例(对象)从中分配空间。Java虚拟机(JVM)的堆中储存着正在运行的应用程序所建立的所有对象,这些对象通过...

2251
来自专栏小樱的经验随笔

【python进阶】Garbage collection垃圾回收1

前言 GC垃圾回收在python中是很重要的一部分,同样我将分两次去讲解Garbage collection垃圾回收,此篇为Garbage collection...

3237
来自专栏Albert陈凯

Scala的编程规范与最佳实践

应用层 80/20原则:80%的代码是 纯函数,其余如处理IO,数据库,用户交互等方面的20%的代码也应该尽量轻量级 培养面向表达式的编程思维,培养函数式编...

3755
来自专栏Python小屋

Python+tensorflow计算整数阶乘的方法与局限性

本文代码主要演示tensorflow的基本用法。 import tensorflow as tf # 创建变量,保存计算结果 start = tf.Variab...

3665
来自专栏编程心路

Java虚拟机内存管理(四)—垃圾回收

Java 虚拟机作为运行 Java 程序抽象出来的计算机,具有内存管理的能力,像内存分配、垃圾回收等这些相关的内存管理问题,Java 虚拟机都会帮我们解决,所以...

1082

扫码关注云+社区

领取腾讯云代金券