16.3 内存的管理和回收
使用Java无须担心如何销毁对象。换句话说,就是Java运行时,无须负责Java对象的内存管理。当Java不再使用某个对象时,它会自动进行垃圾回收。
垃圾回收是一个比较复杂的过程,当程序运行时会自动检查整个内存,检查内存中哪些对象引用不再被使用。一旦检查出来后,便会安全删除这些对象。然而,由于垃圾回收需要占用系统的资源,所以它可能会影响应用程序代码的运行,即如果在执行应用程序代码的过程中,执行垃圾回收,则应用程序代码的执行时间可能延长,这会导致程序运行的延迟。由于不知道何时会进行垃圾回收,因此延迟的时间也是不可预知的。
实时应用程序对时间的要求非常严格,即它们必须在确定的、已知的延迟条件下,执行应用程序代码,因此垃圾回收所引起的不可预知的延迟,就成为一个实时程序致命的问题。
垃圾回收的主要问题,是程序无法估计时间延迟导致程序执行的延迟。能否避免这种问题的发生呢?通过更频繁地进行垃圾回收,就可以限制最大延迟时间。因此,垃圾回收成为可预知的。
注意
虽然有垃圾回收和其他的内存管理技术,Java程序仍然可能存在内存泄漏。例如:在不需要组件对象时,却没将其从容器中移除。
下面是一些Java程序设计中有关内存管理的经验:
❑最基本的建议就是尽早释放无用对象的引用。大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域后,自动设置为null。在使用这种方式时,必须特别注意一些复杂的对象图。例如,数组、队列、树、图等,这些对象之间的相互引用关系较为复杂。对于这类对象,GC(垃圾回收)回收它的效率一般较低,如果程序允许,尽早将不用的引用对象赋为null,这样可以加速GC的工作。例如:
……
A a=new A();
//应用a对象
a=null;//当使用对象a之后主动将其设置为空
……
但要注意,如果“a”是方法的返回值,千万不要做这样的处理,否则从该方法中得到的返回值永远为空,而且这种错误不易被发现,因此这时很难及时抓住、排除“NullPointerException”异常。
❑尽量少用“finalize”函数。“finalize”函数是Java给程序员提供的一个释放对象或资源的机会,但是,它会加大GC的工作量,因此尽量少采用“finalize”方式回收资源。
❑如果需要使用经常用到的图片,可以使用soft应用类型,它尽可能将图片保存在内存中,供程序调用,而不引起“OutOfMemory”。
❑注意集合数据类型,包括数组、树、图、链表等数据结构,这些数据结构对GC来说,回收更为复杂。另外,注意全局变量以及静态变量,这些变量往往容易引起悬挂对象,造成内存浪费。
❑尽量避免在类的默认构造器中创建、初始化大量的对象,防止在调用其自己类的构造器时,造成不必要的内存资源浪费。
❑尽量避免强制系统做垃圾内存的回收(通过显式调用方法“System.gc()”),增长系统做垃圾回收的最终时间,降低系统性能。
❑尽量避免显式申请数组空间,当不得不显式地申请数组空间时,尽量准确地估计出其合理值,以免造成不必要的系统内存开销。
❑尽量在做远程方法调用(RMI)类应用开发时,使用瞬间值(transient)变量,除非远程调用端需要获取该瞬间值变量的值。
❑尽量在合适的场景下,使用对象池技术以提高系统性能,缩减系统内存开销。但是要注意对象池的尺寸不易过大,及时清除无效对象释放内存资源。综合考虑应用运行环境的内存资源限制,避免过高估计运行环境所提供内存资源的数量。