垃圾收集器
下面介绍垃圾收集器的特点和它的执行机制: <BR>垃圾收集器系统有自己的一套方案来判断哪个内存块是应该被回收的,哪个是不符合要求暂不回收的。垃圾收集器在一个 Java 程序中的执行是自动的,不能强制执行,即使程序员能明确地判断出有一块内存已经无用了,是应该回收的,程序员也不能强制垃圾收集器回收该内存块。程序员唯一能做的就是通过调用 System. gc 方法来 " 建议 " 执行垃圾收集器,但其是否可以执行,什么时候执行却都是不可知的。这也是垃圾收集器的最主要的缺点。当然相对于它给程序员带来的巨大方便性而言,这个缺点是瑕不掩瑜的。 垃圾收集器的主要特点有: <BR>1 .垃圾收集器的工作目标是回收已经无用的对象的内存空间,从而避免内存渗漏体的产生,节省内存资源,避免程序代码的崩溃。
2 .垃圾收集器判断一个对象的内存空间是否无用的标准是:如果该对象不能再被程序中任何一个 " 活动的部分 " 所引用,此时我们就说,该对象的内存空间已经无用。所谓 " 活动的部分 " ,是指程序中某部分参与程序的调用,正在执行过程中,尚未执行完毕。
3 .垃圾收集器线程虽然是作为低优先级的线程运行,但在系统可用内存量过低的时候,它可能会突发地执行来挽救内存资源。当然其执行与否也是不可预知的。
4 .垃圾收集器不可以被强制执行,但程序员可以通过调用 System. gc 方法来建议执行垃圾收集器。
5 .不能保证一个无用的对象一定会被垃圾收集器收集,也不能保证垃圾收集器在一段 Java 语言代码中一定会执行。因此在程序执行过程中被分配出去的内存空间可能会一直保留到该程序执行完毕,除非该空间被重新分配或被其他方法回收。由此可见,完全彻底地根绝内存渗漏体的产生也是不可能的。但是请不要忘记, Java 的垃圾收集器毕竟使程序员从手工回收内存空间的繁重工作中解脱了出来。设想一个程序员要用 C 或 C++ 来编写一段 10 万行语句的代码,那么他一定会充分体会到 Java 的垃圾收集器的优点!
6 .同样没有办法预知在一组均符合垃圾收集器收集标准的对象中,哪一个会被首先收集。
7 .循环引用对象不会影响其被垃圾收集器收集。
8 .可以通过将对象的引用变量( reference variables ,即句柄 handles )初始化为 null 值,来暗示垃圾收集器来收集该对象。但此时,如果该对象连接有事件监听器(典型的 AWT 组件),那它还是不可以被收集。所以在设一个引用变量为 null 值之前,应注意该引用变量指向的对象是否被监听,若有,要首先除去监听器,然后才可以赋空值。
9 .每一个对象都有一个 finalize( ) 方法,这个方法是从 Object 类继承来的。
10 . finalize( ) 方法用来回收内存以外的系统资源,就像是文件处理器和网络连接器。该方法的调用顺序和用来调用该方法的对象的创建顺序是无关的。换句话说,书写程序时该方法的顺序和方法的实际调用顺序是不相干的。请注意这只是 finalize( ) 方法的特点。
11 .每个对象只能调用 finalize( ) 方法一次。如果在 finalize( ) 方法执行时产生异常( exception ),则该对象仍可以被垃圾收集器收集。
12 .垃圾收集器跟踪每一个对象,收集那些不可到达的对象(即该对象没有被程序的任何 " 活的部分 " 所调用),回收其占有的内存空间。但在进行垃圾收集的时候,垃圾收集器会调用 finalize( ) 方法,通过让其他对象知道它的存在,而使不可到达的对象再次 " 复苏 " 为可到达的对象。既然每个对象只能调用一次 finalize( ) 方法,所以每个对象也只可能 " 复苏 " 一次。
13 . finalize( ) 方法可以明确地被调用,但它却不能进行垃圾收集。
14 . finalize( ) 方法可以被重载( overload ),但只有具备初始的 finalize( ) 方法特点的方法才可以被垃圾收集器调用。
15 .子类的 finalize( ) 方法可以明确地调用父类的 finalize( ) 方法,作为该子类对象的最后一次适当的操作。但 Java 编译器却不认为这是一次覆盖操作( overriding ),所以也不会对其调用进行检查。
16 .当 finalize( ) 方法尚未被调用时, System. runFinalization( ) 方法可以用来调用 finalize( ) 方法,并实现相同的效果,对无用对象进行垃圾收集。
17 .当一个方法执行完毕,其中的局部变量就会超出使用范围,此时可以被当作垃圾收集,但以后每当该方法再次被调用时,其中的局部变量便会被重新创建。
18 . Java 语言使用了一种 " 标记交换区的垃圾收集算法 " 。该算法会遍历程序中每一个对象的句柄,为被引用的对象做标记,然后回收尚未做标记的对象。所谓遍历可以简单地理解为 " 检查每一个 " 。
19 . Java 语言允许程序员为任何方法添加 finalize( ) 方法,该方法会在垃圾收集器交换回收对象之前被调用。但不要过分依赖该方法对系统资源进行回收和再利用,因为该方法调用后的执行结果是不可预知的。
通过以上对垃圾收集器特点的了解,你应该可以明确垃圾收集器的作用,和垃圾收集器判断一块内存空间是否无用的标准。简单地说,当你为一个对象赋值为 null 并且重新定向了该对象的引用,此时该对象就符合垃圾收集器的收集标准。
判断一个对象是否符合垃圾收集器的收集标准,这是 SUN 公司程序员认证考试中垃圾收集器部分的重要考点(可以说,这是唯一的考点)。所以,考生在一段给定的代码中,应该能够判断出哪个对象符合垃圾收集器收集的标准,哪个不符合。下面结合几种认证考试中可能出现的题型来具体讲解:
Object obj = new Object ( ) ; <BR>我们知道, obj 为 Object 的一个句柄。当出现 new 关键字时,就给新建的对象分配内存空间,而 obj 的值就是新分配的内存空间的首地址,即该对象的值 ( 请特别注意,对象的值和对象的内容是不同含义的两个概念:对象的值就是指其内存块的首地址,即对象的句柄;而对象的内容则是其具体的内存块 ) 。此时如果有 obj = null ; 则 obj 指向的内存块此时就无用了,因为下面再没有调用该变量了。 <BR>请再看以下三种认证考试时可能出现的题型:
程序段 1: <BR>1.fobj = new Object ( ) ; <BR>2.fobj. Method ( ) ; <BR>3.fobj = new Object ( ) ; <BR>4.fobj. Method ( ) ; <BR>问:这段代码中,第几行的fobj 符合垃圾收集器的收集标准? <BR>答:第1行。因为第3行的fobj被赋了新值,产生了一个新的对象,即换了一块新的内存空间,也相当于为第1行中的fobj赋了null值。这种类型的题在认证0考试中是最简单的。
<BR>程序段2: <BR>1.Object sobj = new Object ( ) ; <BR>2.Object sobj = null ; <BR>3.Object sobj = new Object ( ) ; <BR>4.sobj = new Object ( ) ; <BR>问:这段代码中,第几行的内存空间符合垃圾收集器的收集标准? <BR>答:第1行和第3行。因为第2行为sobj赋值为null,所以在此第1行的sobj符合垃圾收集器的收集标准。而第4行相当于为sobj赋值为null,所以在此第3行的sobj也符合垃圾收集器的收集标准。 <BR>如果有一个对象的句柄a,且你把a作为某个构造器的参数,即 new Constructor ( a )的时候,即使你给a赋值为null,a也不符合垃圾收集器的收集标准。直到由上面构造器构造的新对象被赋空值时,a才可以被垃圾收集器收集。 <BR><BR>程序段3: <BR>1.Object aobj = new Object ( ) ; <BR>2.Object bobj = new Object ( ) ; <BR>3.Object cobj = new Object ( ) ; <BR>4.aobj = bobj; <BR>5.aobj = cobj; <BR>6.cobj = null; <BR>7.aobj = null; <BR>问:这段代码中,第几行的内存空间符合垃圾收集器的收集标准? <BR>答:第7行。注意这类题型是认证考试中可能遇到的最难题型了。 <BR>行1-3分别创建了Object类的三个对象:aobj,bobj,cobj <BR>行4:此时对象aobj的句柄指向bobj,所以该行的执行不能使aobj符合垃圾收集器的收集标准。 <BR>行5:此时对象aobj的句柄指向cobj,所以该行的执行不能使aobj符合垃圾收集器的收集标准。 <BR>行6:此时仍没有任何一个对象符合垃圾收集器的收集标准。 <BR>行7:对象cobj符合了垃圾收集器的收集标准,因为cobj的句柄指向单一的地址空间。在第6行的时候,cobj已经被赋值为null,但由cobj同时还指向了aobj(第5行),所以此时cobj并不符合垃圾收集器的收集标准。而在第7行,aobj所指向的地址空间也被赋予了空值null,这就说明了,由cobj所指向的地址空间已经被完全地赋予了空值。所以此时cobj最终符合了垃圾收集器的收集标准。 但对于aobj和bobj,仍然无法判断其是否符合收集标准。 <BR>总之,在Java语言中,判断一块内存空间是否符合垃圾收集器收集标准的标准只有两个: <BR>1.给对象赋予了空值null,以下再没有调用过。 <BR>2.给对象赋予了新值,既重新分配了内存空间。 <BR>最后再次提醒一下,一块内存空间符合了垃圾收集器的收集标准,并不意味着这块内存空间就一定会被垃圾收集器收集</P></TD></TR></TBODY></TABLE>
</span>
2 .垃圾收集器判断一个对象的内存空间是否无用的标准是:如果该对象不能再被程序中任何一个 " 活动的部分 " 所引用,此时我们就说,该对象的内存空间已经无用。所谓 " 活动的部分 " ,是指程序中某部分参与程序的调用,正在执行过程中,尚未执行完毕。
3 .垃圾收集器线程虽然是作为低优先级的线程运行,但在系统可用内存量过低的时候,它可能会突发地执行来挽救内存资源。当然其执行与否也是不可预知的。
4 .垃圾收集器不可以被强制执行,但程序员可以通过调用 System. gc 方法来建议执行垃圾收集器。
5 .不能保证一个无用的对象一定会被垃圾收集器收集,也不能保证垃圾收集器在一段 Java 语言代码中一定会执行。因此在程序执行过程中被分配出去的内存空间可能会一直保留到该程序执行完毕,除非该空间被重新分配或被其他方法回收。由此可见,完全彻底地根绝内存渗漏体的产生也是不可能的。但是请不要忘记, Java 的垃圾收集器毕竟使程序员从手工回收内存空间的繁重工作中解脱了出来。设想一个程序员要用 C 或 C++ 来编写一段 10 万行语句的代码,那么他一定会充分体会到 Java 的垃圾收集器的优点!
6 .同样没有办法预知在一组均符合垃圾收集器收集标准的对象中,哪一个会被首先收集。
7 .循环引用对象不会影响其被垃圾收集器收集。
8 .可以通过将对象的引用变量( reference variables ,即句柄 handles )初始化为 null 值,来暗示垃圾收集器来收集该对象。但此时,如果该对象连接有事件监听器(典型的 AWT 组件),那它还是不可以被收集。所以在设一个引用变量为 null 值之前,应注意该引用变量指向的对象是否被监听,若有,要首先除去监听器,然后才可以赋空值。
9 .每一个对象都有一个 finalize( ) 方法,这个方法是从 Object 类继承来的。
10 . finalize( ) 方法用来回收内存以外的系统资源,就像是文件处理器和网络连接器。该方法的调用顺序和用来调用该方法的对象的创建顺序是无关的。换句话说,书写程序时该方法的顺序和方法的实际调用顺序是不相干的。请注意这只是 finalize( ) 方法的特点。
11 .每个对象只能调用 finalize( ) 方法一次。如果在 finalize( ) 方法执行时产生异常( exception ),则该对象仍可以被垃圾收集器收集。
12 .垃圾收集器跟踪每一个对象,收集那些不可到达的对象(即该对象没有被程序的任何 " 活的部分 " 所调用),回收其占有的内存空间。但在进行垃圾收集的时候,垃圾收集器会调用 finalize( ) 方法,通过让其他对象知道它的存在,而使不可到达的对象再次 " 复苏 " 为可到达的对象。既然每个对象只能调用一次 finalize( ) 方法,所以每个对象也只可能 " 复苏 " 一次。
13 . finalize( ) 方法可以明确地被调用,但它却不能进行垃圾收集。
14 . finalize( ) 方法可以被重载( overload ),但只有具备初始的 finalize( ) 方法特点的方法才可以被垃圾收集器调用。
15 .子类的 finalize( ) 方法可以明确地调用父类的 finalize( ) 方法,作为该子类对象的最后一次适当的操作。但 Java 编译器却不认为这是一次覆盖操作( overriding ),所以也不会对其调用进行检查。
16 .当 finalize( ) 方法尚未被调用时, System. runFinalization( ) 方法可以用来调用 finalize( ) 方法,并实现相同的效果,对无用对象进行垃圾收集。
17 .当一个方法执行完毕,其中的局部变量就会超出使用范围,此时可以被当作垃圾收集,但以后每当该方法再次被调用时,其中的局部变量便会被重新创建。
18 . Java 语言使用了一种 " 标记交换区的垃圾收集算法 " 。该算法会遍历程序中每一个对象的句柄,为被引用的对象做标记,然后回收尚未做标记的对象。所谓遍历可以简单地理解为 " 检查每一个 " 。
19 . Java 语言允许程序员为任何方法添加 finalize( ) 方法,该方法会在垃圾收集器交换回收对象之前被调用。但不要过分依赖该方法对系统资源进行回收和再利用,因为该方法调用后的执行结果是不可预知的。
通过以上对垃圾收集器特点的了解,你应该可以明确垃圾收集器的作用,和垃圾收集器判断一块内存空间是否无用的标准。简单地说,当你为一个对象赋值为 null 并且重新定向了该对象的引用,此时该对象就符合垃圾收集器的收集标准。
判断一个对象是否符合垃圾收集器的收集标准,这是 SUN 公司程序员认证考试中垃圾收集器部分的重要考点(可以说,这是唯一的考点)。所以,考生在一段给定的代码中,应该能够判断出哪个对象符合垃圾收集器收集的标准,哪个不符合。下面结合几种认证考试中可能出现的题型来具体讲解:
Object obj = new Object ( ) ; <BR>我们知道, obj 为 Object 的一个句柄。当出现 new 关键字时,就给新建的对象分配内存空间,而 obj 的值就是新分配的内存空间的首地址,即该对象的值 ( 请特别注意,对象的值和对象的内容是不同含义的两个概念:对象的值就是指其内存块的首地址,即对象的句柄;而对象的内容则是其具体的内存块 ) 。此时如果有 obj = null ; 则 obj 指向的内存块此时就无用了,因为下面再没有调用该变量了。 <BR>请再看以下三种认证考试时可能出现的题型:
程序段 1: <BR>1.fobj = new Object ( ) ; <BR>2.fobj. Method ( ) ; <BR>3.fobj = new Object ( ) ; <BR>4.fobj. Method ( ) ; <BR>问:这段代码中,第几行的fobj 符合垃圾收集器的收集标准? <BR>答:第1行。因为第3行的fobj被赋了新值,产生了一个新的对象,即换了一块新的内存空间,也相当于为第1行中的fobj赋了null值。这种类型的题在认证0考试中是最简单的。
<BR>程序段2: <BR>1.Object sobj = new Object ( ) ; <BR>2.Object sobj = null ; <BR>3.Object sobj = new Object ( ) ; <BR>4.sobj = new Object ( ) ; <BR>问:这段代码中,第几行的内存空间符合垃圾收集器的收集标准? <BR>答:第1行和第3行。因为第2行为sobj赋值为null,所以在此第1行的sobj符合垃圾收集器的收集标准。而第4行相当于为sobj赋值为null,所以在此第3行的sobj也符合垃圾收集器的收集标准。 <BR>如果有一个对象的句柄a,且你把a作为某个构造器的参数,即 new Constructor ( a )的时候,即使你给a赋值为null,a也不符合垃圾收集器的收集标准。直到由上面构造器构造的新对象被赋空值时,a才可以被垃圾收集器收集。 <BR><BR>程序段3: <BR>1.Object aobj = new Object ( ) ; <BR>2.Object bobj = new Object ( ) ; <BR>3.Object cobj = new Object ( ) ; <BR>4.aobj = bobj; <BR>5.aobj = cobj; <BR>6.cobj = null; <BR>7.aobj = null; <BR>问:这段代码中,第几行的内存空间符合垃圾收集器的收集标准? <BR>答:第7行。注意这类题型是认证考试中可能遇到的最难题型了。 <BR>行1-3分别创建了Object类的三个对象:aobj,bobj,cobj <BR>行4:此时对象aobj的句柄指向bobj,所以该行的执行不能使aobj符合垃圾收集器的收集标准。 <BR>行5:此时对象aobj的句柄指向cobj,所以该行的执行不能使aobj符合垃圾收集器的收集标准。 <BR>行6:此时仍没有任何一个对象符合垃圾收集器的收集标准。 <BR>行7:对象cobj符合了垃圾收集器的收集标准,因为cobj的句柄指向单一的地址空间。在第6行的时候,cobj已经被赋值为null,但由cobj同时还指向了aobj(第5行),所以此时cobj并不符合垃圾收集器的收集标准。而在第7行,aobj所指向的地址空间也被赋予了空值null,这就说明了,由cobj所指向的地址空间已经被完全地赋予了空值。所以此时cobj最终符合了垃圾收集器的收集标准。 但对于aobj和bobj,仍然无法判断其是否符合收集标准。 <BR>总之,在Java语言中,判断一块内存空间是否符合垃圾收集器收集标准的标准只有两个: <BR>1.给对象赋予了空值null,以下再没有调用过。 <BR>2.给对象赋予了新值,既重新分配了内存空间。 <BR>最后再次提醒一下,一块内存空间符合了垃圾收集器的收集标准,并不意味着这块内存空间就一定会被垃圾收集器收集</P></TD></TR></TBODY></TABLE>
</span>