a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 155|回复: 1

[其他] JAVA技巧:对JAVA语言中的finalize的详解

[复制链接]
发表于 2012-8-4 12:28:23 | 显示全部楼层 |阅读模式
轨范员都体味初始化的主要性,但经常会健忘同样主要的断根工作。事实下场,谁需要断根一个int 呢?但在使用轨范库时,把一个对象用完后就“弃之失踪臂”的做法并非老是平安的。当然,Java有垃圾收受接管器来收受接管无用对象占有的内存资本。但也有奸细作况:假定你的对象(并非使用 new缴获得了一块“非凡”的内存区域,因为垃圾收受接管器只知道释放那些经由 new分配的内存,所以它不知道该若何释放该对象的这块 “非凡”内存。为了应对这种情形,Java许可你在类中界说一个名为finalize( )的体例。它的工作事理“应该”是这样的:一旦垃圾收受接管器筹备好释放对象占用的存储空间,将首先挪用其 finalize( )体例,而且不才一次垃圾收受接管动作发生时,才会真正收受接管对象占用的内存。所以若是你筹算用 finalize( ),就能在“垃圾收受接管时刻”做一些主要的断根工作。   这里有一个潜在的编程陷阱,因为有些轨范员(出格是 C++轨范员)刚起头可能会误把finalize( )算作C++中的“析构函数”(C++中销毁对象必需用到这个函数)。所以有需要明晰区分一下:在 C++中,对象必然会被“销毁”(如不雅察看序中没有错误的话);而 Java 里的对象却并非老是被“垃圾收受接管”的。或者换句话说:
0 F# i) I) S- Q. f8 c  1. 对象可能不被收受接管。) O% c4 f5 G; m  B0 a
  2. 垃圾收受接管并不等于“析构”。
% P2 Q# h3 h: Z9 [# K! k  谨记这些,你就能远离困扰。这意味着在你不再需要某个对象之前,如不美观必需执行某些动作,那么你得自己去做。Java并未供给“析构函数”或相似的概念,要做近似的断根工作,你必需自己脱手建树一个执行断根工作的通俗体例。例如,假设某个对象在建树过程中,会将自己绘制到屏幕上。若是你不明晰地年夜屏幕年夜将其擦除,它可能永远得不到断根。如不美观在  D( ?2 w0 x- I( [- q
  finalize( )里插手某种擦除功能,当“垃圾收受接管”发生时(不能保证必然会发生),finalize( )获得了挪用,图像就会被擦除。若是“垃圾收受接管”没有发生,图像就会一向保留下来。
& W! s5 C3 _3 Y% p  也许你会发现,只要轨范没有濒临存储空间用完的那一刻,对象占用的空间就总也得不到释放。如不雅察看序执行竣事,而且垃圾收受接管器一向都没有释放你建树的任何对象的存储空间,则跟着轨范的退出,那些资本会全数交还给操作系统。这个策略是适当的,因为垃圾收受接管自己也有开销,若是不使用它,那就不用支出这部门开销了。
: l8 x% s' i9 b9 Ffinalize( )用途何在?
9 Z$ D: X7 Q9 x- s0 ?$ U  此时,你已经年夜白了不应将finalize( )作为通用的断根体例。那么,finalize( )的真正用途是什么呢?
7 u% S  ?2 L0 [9 w/ S2 h  这引出了要记住的第三点:) X: D4 q& a7 W4 r9 M2 `
  3.垃圾收受接管只与内存有关。( e0 E/ P% v% s
  也就是说,垃圾收受接管器存在的独一原因是为了收受接管轨范不再使用的内存。所以对于与垃圾收受接管有关的任何行为来说(尤其是finalize( )体例),它们也必需同内存及其收受接管有关。# o+ `/ L: Z. i: l
  但这是否意味着若是对象中含有其他对象,finalize( )就应该明晰释放那些对象呢?不——无论对象是若何建树的,垃圾收受接管器城市负责释放对象占有的所有内存。这就将对 finalize( )的需求限制到奸细作况之下:你经由过程某种非“建树对象”的体例为对象分配了存储空间。不外,你也看到了,Java一一切皆为对象,那这种奸细作况是怎么回事呢?
" z6 G. I( S5 M  看来之所以要有finalize( ),是因为你可能在分配内存时,采用了近似C说话中的做法而非Java中的凡是做法。这种情形首要发生在使用“本处所法”的情形下,它是在Java中挪用非Java代码的一种体例。本处所法今朝只撑持C和C++。但它们可根柢用其它说话写的代码,所以你现实上可根柢用任何代码。在非Java代滤鱿脯也许会挪用近似C的malloc( )函数,用它分配存储空间,而且除非挪用了free( )函数,否则存储空间将不会获得释放,年夜而造成内存泄露。当然,free( )是C和C++中的函数,所以你需要在finalize( )赌暌姑本处所法挪用它。) q" e5 b9 ]# t2 E5 S- t3 ]
  至此,你或许已经年夜白了不要过多地使用finalize( )的事理了。对,它确实不是进行通俗的断根工作的合适场所。那么,通俗的断根工作应该在哪执行呢?
/ f: ]( _0 x4 o% k" h你必需执行断根 " B6 ]4 H0 t7 r7 Z
  为断根一个对象,用户必需在进行断根的时刻挪用执行断根动作的体例。听起来似乎很简单,但饶暌闺C++中的“析构函数”的概念稍有抵触。在C++中,所有对象城市被销毁,或者说, “应该”被销毁。如不美观在C++中建树了一个局部对象(就是在仓库上建树,Java中可不行),此时的销毁动作发生在以“右花括号”为鸿沟的、此对象浸染域的末尾处进行。如不美观对象是
5 A1 @) C/ g, `7 u0 V  用new建树的(近似于Java),那么当轨范员挪用C++的delete( )时(Java没有这个呼吁),就会挪用响应的析构函数。如不雅察看序员忘了,那媚暌估远不会挪用析构函数,就会呈现内存泄露,对象的其他部门也不会获得断根。这种错误很难跟踪,这也是让 C++轨范员转向 Java的一个首要身分。
1 i: O& @0 K4 E- J  相反,Java不许可建树局部对象,你必需使用new。在Java中,也没有“delete”来释放对象,因为垃圾收受接管器会辅佐你释放存储空间。甚至可以肤浅地认为,恰是因为垃收受接管集机制的存在,使得 Java 没有析构函数。然而,跟着进修的深切,你就会年夜白垃圾收受接管器的存在并不能完全庖代析构函数。(而且你绝对不能直接挪用finalize( ),所以这也不是一个适当的8 B; Q0 l4 Z7 `* T
  路子。)如不美观你但愿进行除释放存储空间之外的断根工作,你仍是得明晰挪用某个适当的Java体例。这就等同于使用析构函数了,而且没有它便利。6 }8 \0 w/ W6 H' K. c' y0 z$ P2 g
  记住,无论是“垃圾收受接管”仍是“终结”,都不保证必然会发生。如不美观Java虚拟机(JVM)并未面临内存耗尽的气象,它是不会华侈时刻在收受接管垃圾以恢复内存上的。
  t5 W$ v% o( A/ J+ A7 o终结前提 4 ]0 d8 n; W# ]7 ?4 b5 ~/ N
$ m5 J2 G2 [7 U& U  V
  凡是,你不能指望finalize( ),你必需建树其它的“断根”体例,而且明晰地挪用它们。看来,finalize( )只能存在于轨范员很难用到的一些艰涩用法里了。不外,finalize( )还有一个有趣的用法,它并不依靠于每次都要对finalize( )进行挪用,这就是对象“终结前提”的验证。
回复

使用道具 举报

 楼主| 发表于 2012-8-4 12:28:24 | 显示全部楼层

JAVA技巧:对JAVA语言中的finalize的详解

</p>  当你对某个对象不再感乐趣,也就是它可以被断根时,这个对象应该处于某种状况,使它占用的内存可以被平安地释放。例如,若是对象代表了一个打开的文件,在对象被收受接管前途序员应该封锁这个文件。只要对象中存在没有被恰当断根的部门,你的轨范就存在很隐晦的错误。finalize( )的价质ё仝于可以用来最终发现这种情形,尽管它并不老是会被挪用。如不美观某次
% ~9 I; E1 y, X; W, Z  finalize( )的动作使得bug被发现,那你就可据此找出问题地址——这才是你真正关心的。
. l/ A+ h5 M' H4 V# N  以下是个简单的例子,示范了可能的使用体例:
3 d) ~+ A9 }% `; J  C( O+ J: u  class Book {* q4 Z# ]- X; F- D
  boolean checkedOut = false;9 V) K  y4 k. l" c8 m
  Book(boolean checkOut) {* H# O* _8 B) z- ^8 d
  checkedOut = checkOut;1 F3 J2 n. j3 r
  }
2 \" ?* U0 {- L& ?  void checkIn() {
8 y& t5 T9 i* a' F% Y7 j6 q  checkedOut = false;$ W) P$ K; y% g! o& j
  }$ T. Z* I, U, e( l
  public void finalize() {
8 M2 W) U- [/ l0 k; a# q6 H- L8 Q  if(checkedOut), f* V* v% s/ T$ R( ^" s
  System.out.println("Error: checked out");2 v, I  G  f7 R3 G, J( t( P
  // Normally, you'll also do this:7 [+ K! M& j/ h  S5 l0 W
  // super.finalized();
6 g+ S2 Z# N4 n" M% M0 |* t" ^* Z  }
& V/ G( [7 h% U* B3 `  }' S: v9 e' K4 j* T/ a8 m) ?0 \4 r
  public class TerminationCondition {
1 s, j! ^) t# }; l/ G  public static void main(String[] args) {6 J& U& |$ h/ b) F% q% w6 [) a
  Book novel = new Book(true);0 p/ i" L) d8 @% G2 W
  // Proper cleanup:
% C- y! q$ D% f( v  F( K! P' K7 ]! h  novel.checkIn();9 w1 |1 l: z" H" ^" j
  // Drop the reference, forget to clean up:
) k  f  I' W; \' T  new Book(true);
% \. T5 }, h9 S  V$ N9 ~% m, g  // Force garbage collection & finalization:
+ m0 \6 H/ J. k  System.gc();
0 u7 b* L- }6 J' Z& P& M* B  }+ [4 M/ l9 I, l' I$ Y: p
  }
2 p- W; F/ r/ S$ _; i  本例的终结前提是:所有的Book对象在被算作垃圾收受接管前都应该被签入(check in)。但在main( )体例中,因为轨范员的错误,有一本书未被签入。若是没有 finalize( )来验证终结前提,将很难发现这种错误。
: p4 _, n- _( P8 a  注重,System.gc( )用于强制终结动作的进行。即使不这么做的话,经由过程一再的执行轨范(假设轨范将分配年夜量的存储空间而导致垃圾收受接管动作的执行),最终也能找犯错误的Book对象。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-21 22:39 , Processed in 0.393719 second(s), 23 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表