Effective Java 2nd笔记第二章第五条:避免使用终结方法

参考了《Java编程思想》P87、《深入理解Java虚拟机》关于垃圾回收器的内容

1、终结方法和垃圾回收器(GC,Garbage Collector)

1.1、垃圾回收器的意义

  对于OOP程序员来说,注意初始化的重要性比较容易,但他们常常会忘记重要的清理工作。所谓清理,也就是回收一个对象所占用的资源(内存资源和非内存资源)。而一旦程序员忘记清理—比如把一个对象用完后就“弃之不顾”,这可能就会导致“内存溢出”等安全问题。
 在C++中,由析构函数(也可叫析构器,destructors)来作为清理的常规方法,它是构造物器必需的对应物,它可以回收对象的内存(也就是销毁对象)及非内存资源。在C++中,必须依靠程序员不搞出缺陷才能保证所有对象都得到销毁:如果在C++中创建了一个局部对象,此时的销毁动作发生在对象作用域的末尾处;如果对象是用new创建的(类似于Java中),那么当程序员调用C++的Delete操作符时(Java没有这个命令),就会调用相应的析构函数,如果程序员忘记调用delete,那么永远不会调用析构函数,这样就会出现内存泄露,而对象占用的其他资源也得不到清理。一旦程序员搞出这样的程序缺陷,往往很难跟踪。而这个麻烦在Java中通过垃圾回收化解。
 在Java中,当没有对象引用指向原先分配给某个对象的内存(我们称之为,对象不可达[unreachable])时,该内存便成为垃圾。JVM的一个系统级线程(GC线程)运行对应的垃圾回收器,当触发了条件后,垃圾回收器便自动释放该垃圾,垃圾回收器的这个自动释放功能叫做垃圾回收。垃圾回收意味着程序不再需要的对象是”无用信息”,这些信息将被丢弃。当一个对象不再被引用的时候,垃圾回收器回收它占领的空间,以便空间被后来的新对象使用。事实上,除了垃圾回收,垃圾回收器也可以清除内存记录碎片。由于创建对象和垃圾回收器释放丢弃对象所占的内存空间,内存会出现碎片。碎片是分配给对象的内存块之间的空闲内存洞。碎片整理将所占用的堆内存移到堆的一端,JVM将整理出的内存分配给新的对象。
 垃圾回收器能自动释放内存空间,减轻编程的负担。这使Java 虚拟机具有一些优点。首先,它能使编程效率提高。在没有垃圾回收机制的时候,可能要花许多时间来解决一个难懂的存储器问题。在用Java语言编程的时候,靠垃圾回收机制可大大缩短时间。其次是它保护程序的完整性, 垃圾回收器是Java语言安全性策略的一个重要部份。
 垃圾回收器的一个潜在的缺点是它的开销影响程序性能。Java虚拟机必须追踪运行程序中有用的对象,而且最终释放没用的对象。这一个过程需要花费处理器的时间。其次垃圾回收算法的不完备性,早先采用的某些垃圾回收算法就不能保证100%收集到所有的废弃内存。当然随着垃圾回收算法的不断改进以及软硬件运行效率的不断提升,这些问题都可以迎刃而解。

1.2、垃圾回收器的重要特性及原理

1.2.1、内存分配和垃圾回收都基于分代策略

 垃圾回收算法

1.2.1 主GC有触发条件

 JVM进行次GC的频率很高,但因为这种GC处理的是生命周期短的小对象,因此占用时间极短,所以对系统产生的影响不大。更值得关注的是主GC的触发条件,因为它处理生命周期较老的对象,对系统影响很明显。总的来说,有两个条件会触发主GC:

  (1)当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。

  (2)Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。

 由于是否进行主GC由JVM根据系统环境决定,而系统环境在不断的变化当中,所以主GC的运行具有不确定性,无法预计它何时必然出现,但可以确定的是对一个长期运行的应用来说,其主GC是反复进行的。

1.3、终结方法

 终结方法,也可以叫终结器(finalizer),没错,它就是Object类里面定义的finalize()方法。有些程序员(特别是C++程序员)刚刚开始可能会把终结方法误以为是C++中的析构函数,其实两者半毛钱关系都没有。
 首先,finalize()的工作是要和垃圾回收器结合起来的,原理大概是这样:垃圾回收器在不断检测没用对象,并将它们排在终结方法队列里,顺便一个个来调用它们的终结方法,一旦垃圾回收被启动,就将队列中所有已经调用过终结方法的无用对象销毁;然后,垃圾回收器只与内存有关,换言之与垃圾回收有关的终结方法也只同内存有关;
最后,要知道的是,在C++中对象一定会被销毁(如果程序没有缺陷的话),析构函数一定会被执行,然而在Java中对象却不一定被回收(只要程序没有频临存储空间用完的那一刻,那么对象就很有可能不被释放),甚至终结方法也不一定会执行(因为要排队。。。)。
 综上所述,终结方法只应与内存有关,并且不一定会被执行,而这两点根析构函数是完全相反的。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值