浅谈JAVA垃圾回收机制

来源:互联网 发布:数据分类 编辑:程序博客网 时间:2024/05/16 04:06

浅谈JAVA垃圾回收机制

垃圾回收机制是JAVA的主要特性之一,在对垃圾回收机制进行概述之后,本文从“失去引用”和“离开作用域”这两个角度分析了JAVA程序中的对象在何种条件下满足垃圾回收的要求。最后,本文简要介绍了垃圾回收机制的两个特性。

 

Java的垃圾回收机制是Java虚拟机提供的能力,用于在空闲时间以不定时的方式动态回收无任何引用的对象占据的内存空间。需要注意的是:垃圾回收回收的是无任何引用的对象占据的内存空间而不是对象本身,很多人来我公司面试时,我都会问这个问题的,70%以上的人回答的含义是回收对象,实际上这是不正确的。System.gc()Runtime.getRuntime().gc()  上面的方法调用时用于显式通知JVM可以进行一次垃圾回收,但真正垃圾回收机制具体在什么时间点开始发生动作这同样是不可预料的,这和抢占式的线程在发生作用时的原理一样。
 

作为一种适应于Internet计算环境、面向对象并具有平台无关性的编程语言,JAVA早已确立了在IT界的地位,并因网络日益广泛的应用而变得越来越重要。因此,在高校中JAVA也逐渐受到更多教师和学生的重视。

实际上,JAVA源自C++语言。但JAVA语言避免了C++中晦涩的结构,成功翻越了多重继承机制的恼人问题;JAVA的垃圾回收机制显著地提高了生产率,降低了复杂度;在网络背景下使用虚拟机,以及有关安全性和动态加载的一系列设计选择,迎合了正在出现的需求和愿望。这些特性使Java不仅成为现有程序员的武器,而且也为新的程序员创造了繁荣的市场空间。在JAVA语言的上述特性中,本文主要分析其垃圾回收机制。

一、JAVA垃圾回收机制概述

在VB、C++等某些程序设计语言中,无论是对象还是动态配置的资源或内存,都必须由程序员自行声明产生和回收,否则其中的资源将不断消耗,造成资源的浪费甚至死机。由于要预先确定占用的内存空间是否应该被回收是非常困难的,这就导致手工回收内存往往是一项复杂而艰巨的工作。因此,当使用这些程序设计语言编程时,程序员不仅要考虑如何实现算法以满足应用,还要花费许多精力考虑合理使用内存避免系统崩溃。

针对这种情况,JAVA语言建立了垃圾回收机制。JAVA是纯粹的面向对象的编程语言,其程序以类为单位,程序运行期间会在内存中创建很多类的对象。这些对象在完成任务之后,JAVA的垃圾回收机制会自动释放这些对象所占用的空间,使回收的内存能被再次利用,提高程序的运行效率。垃圾回收不仅可以提高系统的可靠性、使内存管理与类接口设计分离,还可以使开发者减少了跟踪内存管理错误的时间,从而把程序员从手工回收内存空间的繁重工作中解脱出来。

JAVA垃圾回收机制另一个特点是,进行垃圾回收的线程是一种低优先级的线程,在一个Java程序的生命周期中,它只有在内存空闲的时候才有机会运行。

下面本文从“对象的失去引用”和“对象离开作用域”这两个方面进行分析,探讨JAVA程序中的对象什么时候可以被当作垃圾来进行回收。

二、对象的失去引用

通过下面的一段JAVA程序(例1),我们可以讨论程序中的对象是否已经符合垃圾回收的条件。请注意,我们只是讨论某个对象是否符合被回收的条件,这并不意味着该对象将被立即回收,关于这一点,本文后面还将进一步说明。

    例1:

1.       class Computer{ }

2.       public class GarbageCollector {

3.         Computer makeComputer() {

4.           Computer tempComputer = new Computer ();

5.           return tempComputer;

6.         }

7.         public static void main (String args[]) {

8.           GarbageCollector t = new GarbageCollector ();

9.           Computer newComputer= t.makeComputer();

10.        System.out.println(“tempComputer所指向的对象可以被当作垃圾回收”);

11.        Computer computer1=new Computer();

12.        Computer computer2=new Computer();

13.        computer2= computer1;

14.        Computer computer3=new Computer();

15.        computer3=null;

16.        System.out.println(“computer3所指向的对象可以被当作垃圾回收”);

17.        computer1=null;

18.        System.out.println(“computer1所指向的对象不能被当作垃圾回收”);

19.      }

20.    }

在继续讨论之前,我们首先回顾JAVA的两个基本概念——引用和对象。引用类似于其它程序设计语言中的指针。引用就象一个商标标签或者指示牌,它上面写着物品的名称,比如“电脑”,可是它并不是电脑。当把这个标签贴到电脑上后,标签与电脑就联系起来,它就代表电脑了。因此Computer computer1=new Computer();可理解为先创建一个对象的标签computer1,然后把标签贴到新创建的对象Computer上。当创建的对象内没有任何标签指向它时,就可被当作垃圾进行回收。

电脑2

computer2

基于例1中的第11行~第20行,我们分析一个JAVA对象什么时候处于“失去引用”状态从而满足被回收的条件。

电脑1

computer1

 


 

           图1                                 图2

第11行(见图1), Computer computer1=new Computer();创建了引用(标签)computer1,及对应的对象(真实的第一台电脑),可以形象地理解为把标签computer1贴在第一台电脑上。

第12行(见图2),Computer computer2=new Computer();创建了标签computer2,及对应的对象(真实的第二台电脑),并把标签computer2贴在第二台电脑上。

电脑3

computer3

电脑1

电脑2

computer1

computer2

 


 

图3                                    图4

第13行(见图3),computer2=computer1; 该语句的结果是使引用computer2指向computer1指向的对象。就是说computer2原来指向的对象失去了引用,该对象会被回收。所以,现在就有两个引用computer1和compute2指向原来computer1指向的对象。形象地说,computer1= computer2的意思就是把computer2这个标签撕了下来贴在了computer1标签所在的电脑上,所以电脑2就没有了标签而成为垃圾,电脑1就有了两个标签。

第14行(见图4),Computer computer3=new Computer();创建了标签computer3,及对应的对象(真实的第三台电脑),并把标签computer3贴在第三台电脑上。

电脑1

computer1

computer2

电脑3

computer3

 


 

图5                                     图6

第15行(见图5),computer3=null; 使引用该computer3无指向,该行相当于把computer3标签从电脑上撕了下来。第三台电脑因为失去引用而无法访问,就成为了垃圾而被JAVA自动回收。

第17行(见图6), computer1=null; 该行相当于把computer1标签从电脑上撕了下来。但是computer1标签所在的第一台电脑并没有成为垃圾,因为还有一个computer2标签贴在上面。

应当指出的是,虽然可以通过将对象的引用变量初始化为null值来暗示垃圾收集线程收集该对象,但此时如果该对象连接有事件监听器,那它还是不可以被收集。所以在设一个引用变量为null值之前,应注意该引用变量指向的对象是否被监听,若有,要首先除去监听器,然后才可以赋空值。

三、对象离开作用域

我们知道,在传统的面向过程语言(如C语言)中,当一个方法执行完毕,其中的局部变量就因离开了作用域而被释放;以后当该方法再次被调用时,其中的局部变量会被重新创建。但在VB、C++等引入面向对象方法的编程语言中,其程序中基本类型的变量可以在离开作用域后被自动释放,但对象并不能被自动释放。JAVA语言的垃圾回收线程通过自动跟踪对象的使用情况,能够使程序中的对象类似于基本类型那样在离开作用域之后被回收。

基于例1中的第1行~第10行,可以分析一个JAVA对象如何因“离开作用域”而满足被回收的条件。

临时电脑

tempComputer

 


 

  

图7

第4行(见图7),Computer tempComputer = new tempComputer();创建了引用(标签)tempComputer,及对应的对象(真实的电脑),可以形象地理解为把标签tempComputer贴在临时电脑上。

第9行,Computer newComputer= t.makeComputer();通过调用t的方法makeComputer()来创建一个Computer的对象,并用标签把newComputer贴在该电脑上。对象tempComputer是一个局部变量,xc在方法makeComputer结束后,系统会自动地把函数返回值作一份拷贝复制给调该方法的引用,然后对象tempComputer所被当作垃圾回收。

四、JAVA垃圾回收机制的特点

前面我们讨论了JAVA程序中的对象在什么条件下满足被回收的条件,而垃圾回收线程如何回收这些“垃圾”,则遵循以下两个特性。

1.  自动性。Java技术提供了一个系统级的线程,即垃圾收集器线程,来跟踪每一块分配出去的内存空间,当Java 虚拟机处于空闲循环时,垃圾收集器线程会自动检查每一块分配出去的内存空间,然后自动回收每一块可以回收的无用的内存块。

2.  不可预期性。一个对象成为了垃圾,但是你不能断言,该对象在这行以后就立刻被清除,甚至有可能当程序结束后,该对象仍然占用内存。像Windows这样的软件常常会出现内存不足的情况,JAVA程序很少出现就是因为可以自动回收内存。然而,因为JAVA也不能保证及时地清除无用的对象,所以JAVA程序也会出现内存不足的情况,只是这种情况很少出现。垃圾收集线程在一个Java程序中的执行是自动的,不能强制执行,即使程序员能明确地判断出有一块内存已经无用了,是应该回收的,程序员也不能强制垃圾收集器回收该内存块。程序员唯一能做的就是通过调用System.gc 方法来"建议"执行垃圾收集器,但其是否可以执行,什么时候执行却都是不可知的。

参考文献:

[1] 《成功通过SUN认证JAVA2程序员考试》,张洪斌,北京科海集团公司出品,2002

[2] Java 理论与实践:垃圾收集简史,Brian Goetz,http://www-900.ibm.com/developerWorks/cn/java/j-jtp10283/

[3] 《Java 2考试指南》,William Stanek[美],电子工业出版社,2002

原创粉丝点击