展望2011

2007-01-04 | java中finalize的使用(2) [转]

上一篇 / 下一篇  2007-04-27 17:56:57 / 个人分类:编程基础

理解finalize()-析构函数的替代者

51Testing软件测试网b wl/Q3Up0qFA

by Tim Gooch

$A9|\C+V3c051Testing软件测试网7A(W'A Mn

在许多方面,Java 类似于 C++。Java 的语法非常类似于 C++,Java 有类、方法和数据成员;Java 的类有构造函数; Java 有异常处理。

+Xla @H/GUF"m0

0{ w rO~0但是,如果你使用过 C++ 会发现 Java 也丢掉一些可能是你熟悉的特性。这些特性之一就是析构函数。取代使用析构函数,Java 支持finalize() 方法。

Is&W_d7Zw^/E0

d Zm9\#@4A7Z0在本文中,我们将描述 finalize() 与 C++ 析构函数的区别。另外,我们将创建一个简单的 Applet 来演示 finalize() 是如何工作的。

`'oo!?1LS`0

}.k:e-S8~-vVR D0最终的界限51Testing软件测试网Q*v eB4\Uh:e

$F\.QV Z1~0与 Java 不同,C++ 支持局部对象(基于栈)和全局对象(基于堆)。因为这一双重支持,C++ 也提供了自动构造和析构,这导致了对构造函数和析构函数的调用,(对于堆对象)就是内存的分配和释放。

&U D b-m7Y0

)|\:{1}$^ S0在 Java 中,所有对象都驻留在堆内存,因此局部对象就不存在。结果,Java 的设计者觉得不需要析构函数(象 C++ 中所实现的)。

)y9KN%ROW|#{0

1y%dkG9b%V5K%TA0取而代之,Java 定义了一个特殊的方法叫做finalize() ,它提供了 C++ 析构函数的一些功能。但是,finalize() 并不完全与 C++ 的析构函数一样,并可以假设它会导致一系列的问题。finalize() 方法作用的一个关键元素是 Java 的垃圾回收器。51Testing软件测试网 Q`iIy5pbnN

51Testing软件测试网0^0Ky(t"d$Y

垃圾回收器51Testing软件测试网 e2P)nthY

!L"lD*h(nw1I0在 C/C++、Pascal和其他几种多种用途的编程语言中,开发者有责任在内存管理上发挥积极的作用。例如,如果你为一个对象或数据结构分配了内存,那么当你不再使用它时必须释放掉该内存。

&qF ^^3mS051Testing软件测试网&`-F.T*C%Gg

在 Java 中,当你创建一个对象时,Java 虚拟机(JVM)为该对象分配内存、调用构造函数并开始跟踪你使用的对象。当你停止使用一个对象(就是说,当没有对该对象有效的引用时),JVM 通过垃圾回收器将该对象标记为释放状态。51Testing软件测试网Kh`%iOy

B| qQ#C8L/?0当垃圾回收器将要释放一个对象的内存时,它调用该对象的finalize() 方法(如果该对象定义了此方法)。垃圾回收器以独立的低优先级的方式运行,只有当其他线程挂起等待该内存释放的情况出现时,它才开始运行释放对象的内存。(事实上,你可以调用System.gc() 方法强制垃圾回收器来释放这些对象的内存。)51Testing软件测试网3H&m DJw'r(}8I

"? kAx#g0在以上的描述中,有一些重要的事情需要注意。首先,只有当垃圾回收器释放该对象的内存时,才会执行finalize()。如果在 Applet 或应用程序退出之前垃圾回收器没有释放内存,垃圾回收器将不会调用finalize()。

4Ol(LY#{8M;I0

TWB MX p0P%LW0其次,除非垃圾回收器认为你的 Applet 或应用程序需要额外的内存,否则它不会试图释放不再使用的对象的内存。换句话说,这是完全可能的:一个 Applet 给少量的对象分配内存,没有造成严重的内存需求,于是垃圾回收器没有释放这些对象的内存就退出了。51Testing软件测试网0tQO2R)_

51Testing软件测试网F&W R!S!{8q)bsBx

显然,如果你为某个对象定义了finalize() 方法,JVM 可能不会调用它,因为垃圾回收器不曾释放过那些对象的内存。调用System.gc() 也不会起作用,因为它仅仅是给 JVM 一个建议而不是命令。

P:_YC k,D-zf0

i_{N0s$]-l0finalize() 有什么优点呢?

4pU.w6L%b`%] bv8g0

r!u Y!df|P[0如果finalize() 不是析构函数,JVM 不一定会调用它,你可能会疑惑它是否在任何情况下都有好处。事实上,在 Java 1.0 中它并没有太多的优点。51Testing软件测试网e!B8?i4^6qCS

$}-q.ot7HV f&V\0根据 Java 文档,finalize() 是一个用于释放非 Java 资源的方法。但是,JVM 有很大的可能不调用对象的finalize() 方法,因此很难证明使用该方法释放资源是有效的。51Testing软件测试网 p&xF_J

O:b8qQT0Java 1.1 通过提供一个System.runFinalizersOnExit() 方法部分地解决了这个问题。(不要将这个方法与 Java 1.0 中的System.runFinalizations() 方法相混淆。)不象System.gc() 方法那样,System.runFinalizersOnExit() 方法并不立即试图启动垃圾回收器。而是当应用程序或 Applet 退出时,它调用每个对象的finalize() 方法。

*m \Z-OEG!R)xOvm051Testing软件测试网HfL9u5H+a

正如你可能猜测的那样,通过调用System.runFinalizersOnExit() 方法强制垃圾回收器清除所有独立对象的内存,当清除代码执行时可能会引起明显的延迟。现在建立一个示例 Applet 来演示 Java 垃圾回收器和finalize() 方法是如何相互作用的。

1W%X+p qU#Nc;f2N051Testing软件测试网_2Y E @r8TE

回收垃圾

m'y`3S?u-h#Mx051Testing软件测试网 zx-]dG0Q#l.G

通过使用Java Applet Wizard 创建一个新的 Applet 开始。当提示这样做时,输入final_things作为 Applet 名,并选择不要生成源文件注释。

-|6@)~Y4e"k$Zh051Testing软件测试网!K cF(V tF*K8qx

接下来,在Java Applet Wizard 进行第三步,不要选择多线程选项。在第五步之前,根据需要修改 Applet 的描述。

:gcYT(y5ctI s9w0

dpby_x-m[:?:m q)X0当你单击Finish 后,Applet Wizard 将生成一个新的工作空间,并为该项目创建缺省的 Java 文件。从列表 A 中选择适当的代码输入(我们已经突出显示了你需要输入的代码)。51Testing软件测试网mi4T-G8xT

51Testing软件测试网+O:n z F#b @0f OJ Y

当你完成代码的输入后,配置Internet 浏览器将System.out 的输出信息写到Javalog.txt 文件中。(在IE 选项对话框的高级页面中选择起用 Java Logging。)

c.vIj WA6LM Q0

'BZwwd rcD0编译并运行该 Applet。然后,等待 Applet 运行(你将在状态栏中看到 Applet 已启动的信息),退出浏览器,并打开Javalog.txt 文件。你将会发现类似于下列行的信息:

6^*O+pOQ\-y {x051Testing软件测试网'm&r,_:k%OK

        1000 things constructed51Testing软件测试网{{2m"B b

51Testing软件测试网lbn$H+y,[

        0 things finalized51Testing软件测试网-^,TO$ca sN G

51Testing软件测试网kkT4X6IV{/S9m y0Z

正如你能够看到的那样,建立了1,000个对象仍然没有迫使垃圾回收器开始回收空间,即使在 Applet 退出时也没有对象被使用。51Testing软件测试网NF3c gM#sv

:l+^m!@@%o5v5v+X0现在,删除在stop() 方法第一行中的注释符以起用System.gc() 方法。再次编译并运行该 Applet ,等待 Applet 完成运行,并退出浏览器。当你再次打开Javalog.txt 文件,你将看到下列行:

B5IJ'g,|l0

2hHYfwv|"\~0        1000 things constructed51Testing软件测试网5L&\8akXF y

gN0a hz0        963 things finalized51Testing软件测试网|?rcR9{

51Testing软件测试网 L*jM%s?nb

这次,垃圾回收器认为大多数对象未被使用,并将它们回收。按顺序,当垃圾回收器开始释放这些对象的内存时,JVM 调用它们的finalize() 方法。

n!M0` U7S051Testing软件测试网-z&VUQ1J4X

继承finalize()?51Testing软件测试网;Y7|_ K{+X)x(R&}

51Testing软件测试网+l,LC?Tsan

顺便,如果你在类中定义了finalize() ,它将不会自动调用基类中的方法。在我们讨论了finalize() 与 C++ 的析构函数的不同点后,对这个结论不会惊讶,因为为某个类定制的清除代码另一个类不一定会需要。51Testing软件测试网.z(c E2AH~uB$wv

51Testing软件测试网"F P-zRL5LX4Y9Dk

如果你决定要通过派生一个类的finalize() 方法来调用基类中的finalize() 方法,你可以象其他继承方法一样处理。51Testing软件测试网gQ6J8A^+B`

4Wd7wE6Uu0        protected void finalize()

!cy4qT7VY l0

O4MQ:UbKu5J0        {51Testing软件测试网3X9x [{:d(z1y\

51Testing软件测试网[X!AGP-M0TR

          super.finalize();51Testing软件测试网o1^5Y&AUX0W"B

&|`r!h]']+?&X0          // other finalization code...51Testing软件测试网,{-eP\1V['C)@

i!EowVN0        }

[X!oGG J2|3c051Testing软件测试网9Ise#f w

除了允许你控制是否执行清除操作外,这个技术还使你可以控制当前类的finalize() 方法何时执行。51Testing软件测试网0j%t b.x2kcKjN?

51Testing软件测试网.j2z8M"Xu-{}3cz}

结论51Testing软件测试网w-MfB \6qg:_:t q

Gf^R`0然而有益的是,Java 的自动垃圾回收器不会失去平衡。作为便利的代价,你不得不放弃对系统资源释放的控制。不象 C++ 中的析构函数,Java Applet 不会自动执行你的类中的finalize() 方法。事实上,如果你正在使用 Java 1.0,即使你试图强制它调用finalize() 方法,也不能确保将调用它。

k5{JE\"OT([{O.g)i:Z0

C(G3^#e-\BRypt5P0因此,你不应当依靠finalize() 来执行你的 Applet 和应用程序的资源清除工作。取而代之,你应当明确的清除那些资源或创建一个try...finally 块(或类似的机制)来实现。51Testing软件测试网-Qm+q&taJ,Q


TAG: JAVA finalize 编程基础

 

评分:0

我来说两句

Open Toolbar