think in java 第四章 初始化和清除 第三部分

think in java 第四章的学习

清除:收尾和垃圾收集:finalize()用途何在,必须执行清除(普通的清除工作)

 

清除:收尾和垃圾收集

程序员都知道“初始化”的重要性,但通常忘记清除的重要性。毕竟,谁需要来清除一个int呢?但是对于库来说,用完后简单地“释放”一个对象并非总是安全的。当然,Java可用垃圾收集器回收由不再使用的对象占据的内存。现在考虑一种非常特殊且不多见的情况。假定我们的对象分配了一个“特殊”内存区域,没有使用new。垃圾收集器只知道释放那些由new分配的内存,所以不知道如何释放对象的“特殊”内存。为解决这个问题,Java提供了一个名为finalize()的方法,可为我们的类定义它。在理想情况下,它的工作原理应该是这样的:一旦垃圾收集器准备好释放对象占用的存储空间,它首先调用finalize(),而且只有在下一次垃圾收集过程中,才会真正回收对象的内存。所以如果使用finalize(),就可以在垃圾收集期间进行一些重要的清除或清扫工作。
但也是一个潜在的编程陷阱,因为有些程序员(特别是在C++开发背景的)刚开始可能会错误认为它就是在C++中为“破坏器”(Destructor)使用的finalize()——破坏(清除)一个对象的时候,肯定会调用这个函数。但在这里有必要区分一下C++和Java的区别,因为C++的对象肯定会被清除(排开编程错误的因素),而Java对象并非肯定能作为垃圾被“收集”去。或者换句话说:

垃圾收集并不等于“破坏”!

若能时刻牢记这一点,踩到陷阱的可能性就会大大减少。它意味着在我们不再需要一个对象之前,有些行动是必须采取的,而且必须由自己来采取这些行动。Java并未提供“破坏器”或者类似的概念,所以必须创建一个原始的方法,用它来进行这种清除。例如,假设在对象创建过程中,它会将自己描绘到屏幕上。如果不从屏幕明确删除它的图像,那么它可能永远都不会被清除。若在finalize()里置入某种删除机制,那么假设对象被当作垃圾收掉了,图像首先会将自身从屏幕上移去。但若未被收掉,图像就会保留下来。所以要记住的第二个重点是:

我们的对象可能不会当作垃圾被收掉!

有时可能发现一个对象的存储空间永远都不会释放,因为自己的程序永远都接近于用光空间的临界点。若程序执行结束,而且垃圾收集器一直都没有释放我们创建的任何对象的存储空间,则随着程序的退出,那些资源会返回给操作系统。这是一件好事情,因为垃圾收集本身也要消耗一些开销。如永远都不用它,那么永远也不用支出这部分开销。

我的理解:

这段内容基本上都是围绕着finalize这段方法进行叙述的。

在单独使用java的情况时,很少遇到对象无法回收的问题。但是在一些特殊情况下,如果有非java资源需要被释放,就需要使用这个方法。

比如说使用JNI的情况下,C或者C++和java共同使用,C的部分很可能会在java栈内存中开辟空间,java的GC只作用于堆,所以栈中的内存不会通过GC去自动回收…这时候我们怎么去回收栈内存中的资源呢?当然可以使用C中的delete()方法,也可以使用java中的finalize()方法在java中进行回收处理。

又或者说,为了拿到数据,我想要打开一个文件,当我执行GC时,我想关闭这个文件,这时候就可以使用finalize()自定义这个关闭操作。

怎么说呢,总感觉我使用finalize()的时候很少,适用于一些特殊的业务。

1.finalize()

finalize()有如下要点:

(1)对象不一定会被回收。
(2)垃圾回收不是析构函数。
(3)垃圾回收只与内存有关。
(4)垃圾回收和finalize()都是靠不住的,只要JVM还没有快到耗尽内存的地步,它是不会浪费时间进行垃圾回收的。

当垃圾回收器(garbage colector)决定回收某对象时,就会运行该对象的finalize()方法。值得C++程序员注意的是,finalize()方法并不能等同与析构函数。Java中是没有析构函数的。C++的析构函数是在对象消亡时运行的。由于C++没有垃圾回收,对象空间手动回收,所以一旦对象用不到时,程序员就应当把它delete()掉。所以析构函数中经常做一些文件保存之类的收尾工作。但是在Java中很不幸,如果内存总是充足的,那么垃圾回收可能永远不会进行,也就是说finalize()可能永远不被执行,显然指望它做收尾工作是靠不住的。

我的理解:

之前看了看一点深入理解java虚拟机,感觉GC真的不是特别靠谱。

首先这个GC根本不是人为控制的(虽然可以通过jvm调优来优化GC策略),你就算调用System.GC(),这个东西只是告诉jvm:“我想要进行GC回收,您行行好吧!”最终决定权还是在jvm那里,等到回收策略到了才进行回收。如果没有调优,一般的jvm都是感觉到内存不足的情况下才回去进行GC的。所以这就意味着finalize()并不能在你想要执行的时候准时执行,可靠性存在很大的问题。

所以说,think in java也说了,我们或许不必过多地使用finalize(),因为它并不是进行普通清除工作的理想场所。那么,普通的清除工作应在何处进行呢?

2.必须执行清除(普通的清除工作)

首先要说明,因为java已经历经很多个版本了,jvm的特性早已改变,我认为已经不是think in java中可以解释的了…

所以我只是挑选一些清除的概念进行学习。

我的理解:

java中的主类型的句柄都在栈内存中分配,存在其作用域,如果超出作用域即释放

java中的new创建的对象都在堆内存中分配,由GC负责回收,GC有其策略。

C/C++中不存在自动回收机制,所以需要手动实用delete()回收内存。

3.总结

主要讲的是一些java资源回收的错误,希望能通过学习jvm进一步了解。

发表评论

电子邮件地址不会被公开。 必填项已用*标注