程序员都了解初始化的重要性,但往往会忘记同样也重要的清理工作。
需要清理一个int,把一个对象用完后就“弃之不顾”的做法并非总是安全的。
Java有垃圾回收器负责回收无用对象占据的内存资源。
对象(并非使用new)获得一块“特殊”的内存域,由于垃圾回收器只知道释放那些经由new分配内存,所以不知道 如何释放该的这块“特殊”内存。
只要程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得不到释放。如果程序执行结束,并且垃圾回收器一直都没有释放你创建的任何对象的存储空间,则随着程序的退出,那些资源也会全部交还操作系统。这个策略是怡当的,因为垃圾回收本身也有开销,要是不使用它,那就不用不用支付这部分开销。
Java里的对象却并非总是被垃圾回收:
工作原理:
一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用finalize()方法,并且在下一次垃圾回收动作发时,才会真正回收对象占用的内存。
finalize()的用途
无论对象是如何创建时,垃圾回收器都会负责释放占据内存。这就将对finalize()的需求限制到一种特殊情况,即通过某种创建对象方式以外的方式为对象分配存储空间。
必须实试清理
要清理一个对象,用户不许在需要清理的时刻调用执行清理动作的方法。在Java中,对象是用new创建,就会调用相应析构函数。如果程序员忘记调用delete,那么永远不会调用析构函数,这样就会出现内存泄漏,对象的其他部分也不会得到清理。也没有释放输出的delete,因为垃圾回收器会帮助你释放存储空间。正是垃圾回收集机制的内存,使得Java没有析构函数。垃圾回收器的存在并不能完全替代析构函数。
记住,无论是垃圾回收还是终结,都不能保证一定会发生。如果Java虚拟机并未面临内存耗尽的情形,它是不会浪费时间去执行垃圾回收以恢复内存的
终结条件
这个对象应该处于某种状态,使它占用的内存可以被安全地释放。
垃圾回收如何工作
Java中所有对象都在堆上分配的方式也非常高昂。垃圾回收器对于提高对象的创建速度。
Java中的堆未必完全像传送带那样工作。是那样的说,势必会导致频繁的内存页面调度——将其移到出硬盘,因此会显得需要拥有比实际需要的内存。最终,在创建了足够的对象之后,内存资源耗尽。其中秘密在于垃圾回收器的介入。当它工作时,一面回收空间,一面使堆中的对象紧奏排序。通过垃圾回收对对象重新排列,实现了一种高速的,有无限空间空间可供分配的堆模型。
引用记数是一种简单但速度很慢的垃圾回收技术。每个对象都含有一个引用计算器,当有引用连接至对象时,引用计算加1,当引用离开作用域或被置为null时,引用计算减1。这种方法有个缺陷,如果对象之间存在循环作用,可能会出现“对象应该被回收,但引用计算不为0”的情况。引用记数常用来说明垃圾收集的工作方式,但几乎从未被应用任何一种Java虚拟机实现中。
Java虚拟机将采用一种自适应的垃圾回收技术。有一种做法叫法停止——复制。这意味着,先暂停程序的运行(所以它不属于后台回收模式),然后将所有存在的活对象从当前堆复制到另一个堆,没有被复制的全部都是垃圾。
当对象被复制到新堆时,他们的一个爱着一个,新堆保持排列,可以按前述方法简单,直接的分配新空间,指向它的那些引用都必须修正。位于堆或静态储区的引用可以直接被修正。旧地址射出新地址。
对于“复制式回收器”而言,效率会降低,两个堆分离之间来回倒腾,从而得到维护比实际需要多一杯的空间。从堆中分配几块较大的内存,复制动作在这些大款内存之间。
一些Java虚拟机会进行检查:要是没有新垃圾产生,就会转换到另一种工作模式(即“子适应”),这种模式称为“标记—清扫”。速度相当慢。所依据的思路同样是从堆栈和静态存储区出发,遍历所有的引用,进而找出所有存的对象。每当它找到一个存活的对象,就会给对象设一个标记,这个过程中不会回收任何对象。只有全部标记工作完成的时候,清理动作才会开始。
“停止——复制”是这种垃圾回收动作不是后台运行的;相反,垃圾回收复制发生的同时,程序将会被暂停。严格来说:要求在释放旧有对象之前,必须先把所有存活对象从旧堆复制新堆,这将导致大量内存复制行为。有了块之后,垃圾回收器在回收的时候就可以往废弃的块里拷贝导致。