程序员都知道“初始化”的重要性,但通常忘记清除的重要性。毕竟,谁需要来清除一个int 呢?但是对于
库来说,用完后简单地“释放”一个对象并非总是安全的。当然,Java 可用垃圾收集器回收由不再使用的对
象占据的内存。现在考虑一种非常特殊且不多见的情况。假定我们的对象分配了一个“特殊”内存区域,没
有使用new。垃圾收集器只知道释放那些由new 分配的内存,所以不知道如何释放对象的“特殊”内存。为
解决这个问题,Java 提供了一个名为finalize()的方法,可为我们的类定义它。在理想情况下,它的工作原
理应该是这样的:一旦垃圾收集器准备好释放对象占用的存储空间,它首先调用finalize(),而且只有在下
一次垃圾收集过程中,才会真正回收对象的内存。所以如果使用finalize(),就可以在垃圾收集期间进行一
些重要的清除或清扫工作。
上面这段文字是Think in java 4.3 清除:收尾和垃圾收集 一章中的第一段.
主要是想看看大牛们对垃圾回收器的看法, 因此, 没有循证原版Think in java中是怎么样描述这个问题的.
当然, 这个问题也可能是由于jdk的版本引起的误差, 因此, 题目只是想吸引大家进来看看...呵呵请注意红字标明的部分, 我们可以看出, 作者(或者是翻译的误差? 这里就这么用用吧, 呵呵)认为: 垃圾回收器在运行时, 会首先调用对象的finalize方法, 在下一次垃圾回收器运行时, 释放该对象的内存.为了照顾各位的情绪, 我还是把问题先在这里描述一下吧, 测试代码在Garbage类中的两个System.gc()的位置, 那里也有些注释的.
 * 我对jdk6.0_update_10的GC的运行机制的推测.
 * 1. 收集需要回收的对象, 并以某种方式记录下来.
 * 2. 调用上次记录在案的需要回收的对象的finalize方法.
 * 3. 回收已经调用过finalize方法的需要回收的对象.
 * 4. 将已经释放的对象从那个"记录"中移除.
 * 下一次运行, 继续1-4的步骤.
我下面引入程序代码(基本上是Think in java中的源代码, 对其中有些东西做了一点小的调整, 效果比较明显一点.):
1. Chair类, 我们就是用它来模拟垃圾的.package selfimpr.ThinkInJava.gc;public class Chair {
static boolean gcrun = false;
static boolean f = false;
static int created = 0;
static int finalized = 0;
int i;
Chair() {
i = ++created;
if(created == 35000)
System.out.println("Created 35000");
}
protected void finalize() {
//检测gc的第一次运行,gc第一次运行时调用finalize方法会触发并输出
if(!gcrun) {
gcrun = true;
System.out.println("Beginning to finalize after " +
created + " Chairs have been ced. ");
}
//监测收集编号i值为35000的Chair对象.
if(i==35000) {
System.out.println("Finalizing Chair #35000, " + "i" + i + " " + created +
"Setting flag to stop Chair creation");
//当i值为35000的Chair的对象被垃圾收集后, 将f设置为true, 结束创建.
f = true;
}
//记录销毁对象的数目
finalized ++;
if(finalized >= created-100) 
System.out.println("All " + finalized + " finalized");
}
}
2. 主类, 控制程序监测GC运行package selfimpr.ThinkInJava.gc;public class Garbage { public static void main(String[] args) {
if(args.length == 0) {
System.err.println("Usage: \n" +
"java Garbage before\n or:\n" +
"java Garbage after");
return ;
}
//1. Chair.f为假时不断的创建Chair和String对象.
while(!Chair.f) {
new Chair();
new String("To take up space");
}
//结束创建活动后, 打印总共被创建了多少Chair对象.
//在这个过程中, JVM自动运行的垃圾回收回收了多少个Chair对象.
System.out.println("After all Chairs have been created:\n" +
"total created = " + Chair.created + 
", total finalized = " + Chair.finalized);
if(args[0].equals("before")) {
System.out.println("gc():");
System.gc();
System.out.println("runFinalization():");
System.runFinalization();
}
//这个地方进行了改动, 下面这两行代码是新加的.
//这里我做了两次测试
/*
 * 第一次, 调用一次System.gc();
 * 第二次, 调用两次System.gc();
 * 然而, 和Think in java的讲法不同, 我调用第一System.gc()的时候, 
 * 程序中没有释放的其他对象的finalize方法并没有被调用.
 * 而我调用两次System.gc();所有没有被释放的对象的finalize方法都被调用了.
 * 
 * 因此, 感觉至少在jdk6.0_update_10中, GC的运行是在第二次的时候,
 * 才调用finalize方法并释放内存的.
 */
System.gc();
System.gc();

//被改动的代码结束

System.out.println("bye!");
if(args[0].equals("after"))
System.runFinalizersOnExit(true);
}}

解决方案 »

  1.   

    难道你不知System.gc()并不保证立即执行的?
      

  2.   

    它是异步的,程序这样写只是提醒,至于是否调用由JVM决定。
      

  3.   


    呵呵, 是不保证立即执行, 但是,你可以看完内容吗? System.gc()会尽它最大的努力去回收垃圾, 这一句是从Java SE api中拿出来的, 而且, 它是否立即执行, 我感觉和具体的运行机制没有太大关联啊...
      

  4.   

    我猜测试的时候可以换种方式,因为Chair类依然是使用new来创建。还有种情况,创建一些新对象,将其放入一个list,然后将list = null来查看list中的对象是如何回收的
      

  5.   

    gc()Runs the garbage collector. Calling this method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Java Virtual Machine has made its best effort to recycle all discarded objects. 
    The name gc stands for "garbage collector". The Java Virtual Machine performs this recycling process automatically as needed even if the gc method is not invoked explicitly. The method System#gc() is the conventional and convenient means of invoking this method. 这是JDK里对System.gc()写的注释,注意黑体标记的suggest. 到底是不是马上回收,还是未知数.
      

  6.   

    它的工作原理“应该”是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize( )方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。所以要是你打算用finalize( ),就能在“垃圾回收时刻”做一些重要的清除工作。这是原文
    When the garbage collector is ready to release the storage used for your object, it will first call finalize( ), and only on the next garbage-collection pass will it reclaim the object’s memory. So if you choose to use finalize( ), it gives you the ability to perform some important cleanup at the time of garbage collection.
    我认为 作者的意思并不是说在回收一个对象的时候垃圾回收器会调用两次 第一次执行finalize( )并记录 第二次再回收内存 而是说当垃圾回收器准备回收对象的时候先会调用finalize()方法 然后再回收 强调的是回收之前必然会调用finalize()方法 而调用finalize()应该并不算在一次garbage-collection pass中 而可能是两次间隙中对不同对象陆续调用的过程
      

  7.   

    我的理解和# knightzhuwei# (守望者)相同书上作者这段话的意思是一个对象被回收之前一定先finalize();
    至于是第一gc()的时候finalize();然后在第二次gc()的时候才回收对象
    还是其他的另外的顺序,
    作者没有深究;只是说finalize()一定在回收之前;不过楼主想要知道到底怎么gc(),你的代码似乎也不能说明啥问题嘛,
    去看看gc()部分的相关源代码,应该能了解吧;
      

  8.   


    Runtime.gc()是一个native的方法,看不到源码
      

  9.   

    一般不需要手工回收的。JVM会自动帮你回收没有引用的对象。
      

  10.   

           /*
             * 第一次, 调用一次System.gc();
             * 第二次, 调用两次System.gc();
             * 然而, 和Think in java的讲法不同, 我调用第一System.gc()的时候, 
             * 程序中没有释放的其他对象的finalize方法并没有被调用.
             * 而我调用两次System.gc();所有没有被释放的对象的finalize方法都被调用了.
             * 
             * 因此, 感觉至少在jdk6.0_update_10中, GC的运行是在第二次的时候,
             * 才调用finalize方法并释放内存的.
             */第一次调用System.gc(),但是JVM并不保证就会马上执行回收呀,但也有可能马上就回收。
      

  11.   

    D:\work\test\test\classes>java classdemo.SystemGCdemo befordsf
    Beginning to finalize after 25771 Chairs have been ced.
    Created 35000
    Finalizing Chair #35000, i 35000 54222 Setting flag to stop Chair creation
    After all Chairs have been created:
    total created = 55039, total finalized = 4458
    bye!D:\work\test\test\classes>java classdemo.SystemGCdemo befordsf
    Created 35000
    Beginning to finalize after 35091 Chairs have been ced.
    Finalizing Chair #35000, i 35000 53552 Setting flag to stop Chair creation
    After all Chairs have been created:
    total created = 60150, total finalized = 3838
    bye!D:\work\test\test\classes>java classdemo.SystemGCdemo befordsf
    Beginning to finalize after 23982 Chairs have been ced.
    Created 35000
    Finalizing Chair #35000, i 35000 52284 Setting flag to stop Chair creation
    After all Chairs have been created:
    total created = 59621, total finalized = 5362
    bye!
    All 59521 finalizedD:\work\test\test\classes>java classdemo.SystemGCdemo befordsf
    Created 35000
    Beginning to finalize after 35091 Chairs have been ced.
    Finalizing Chair #35000, i 35000 53455 Setting flag to stop Chair creation
    After all Chairs have been created:
    total created = 59512, total finalized = 2226
    bye!
    All 59412 finalized
    All 59413 finalized
    楼主可以多次执行以下,结果并不是唯一的。
      

  12.   

    你看的是中文版的Think in java
      

  13.   

    没有看太多的资料,我的理解:gc是由jvm自己控制的,而且在程序中,要尽量少发生gc,在编程中,只有一些重要(贵)的资需要程序员去管理一下,用过后要马上释放它,但你释放的只是对内存的引用,(不知道这样说对不对),如果以后还要调用同一个对象,在gc之前,调用的应该是同一个对象,
      

  14.   

    JMX等可以强制进行GC,普通的操作无法让JVM马上做
      

  15.   

    太深奥了,我对GC的理解是,JVM遍历内存中所有对象,如果有释放标记的就释放,没有的就判断是否需要释放,如果需要释放则标记上下次释放,没有就不鸟它
      

  16.   

    finalize() 并不在java虚拟机的控制范围内!不建议计数使用.
      

  17.   

    System.gc()只是java虚拟机释放无引用地址的资源;finalize()是在系统内存回收垃圾时执行.2个时刻不相同!!!
      

  18.   

    就SUN的JRE来说
    标记垃圾跟回收垃圾其实是2个并行的过程,就算在正常运行中,也会标记垃圾
    只有在可用内存不足的时候,才会把这些垃圾一起回收掉
    LZ的结论:垃圾回收器在运行时, 会首先调用对象的finalize方法, 在下一次垃圾回收器运行时, 释放该对象的内存.
    是错误的
    另外,垃圾收集的算法太多了,单纯讨论这个没什么意义
      

  19.   

    http://www.leyis.cn/soft/?33772.htm 
      

  20.   

    首先,finalize方法并不推荐普通编程的时候使用。
    也就是说,别指着finalize方法释放资源,这是不安全的。当然,finalize方法肯定会在对象内存被释放之前调用。除了这个之外,还有以下几点需要注意的:
    1)应该是只有一个类覆盖了Object类的finalize方法后,这个方法才会被调用。否则这个方法不会被调用(这是我的猜测,因为这样可以简化垃圾回收的过程,为什么看下面)
    2)当一个对象满足被回收的条件后(不准确的说也就是没有任何引用指向它),当Java进行垃圾回收的时候,如果这个对象的类没有覆盖finalize方法,那么这个对象的内存将直接被释放。否则Java将调用finalize方法,并且不释放其占用的内存。这时候使用有了变化,最简单的情况是:
    3)下次垃圾回收的时候将已经调用过finalize方法的这个对象占用的内存释放,但是不会再次调用finalize方法。比较复杂的情况是:4)finalize方法可以让对象复活。就是说,在finalize方法中,如果又让某个可用的引用指向这个对象(比如加到一个队列中),那么这个对象就复活了,不会被下次垃圾回收而释放。
    4)finalize方法只会调用一次。当这个复活对象再次满足垃圾回收的条件后,这时候Java不会再次调用finalize方法。就是说,一个复活后的对象,在垃圾回收之前其finalize方法不会再次被调用。这就是一个潜在的漏洞,也是为什么finalize方法不值得推荐的理由。
    一个对象如果复活了,那么很可能会没有释放相应的资源,但是当这个对象再次没用的时候,其finalize方法将不会被再次调用。当然,用户可以确定说在finalize方法中不会造成对象复活,但是谁能保证在编写代码的时候时刻谨记这点呢?不要越雷池半步的诀窍就是不要靠近雷池。所以finalize方法不推荐使用。
      

  21.   

    所有的书都写着finalize方法会被调用,但实际上finalize是不会保证被调用的。这个是在JVM上测试得到的结果。
      

  22.   

    想理解这个问题需要有多线程的知识
    你写的finalize() 是被gc线程调用的另,java中的变量仅占用栈内存或堆内存---
     new  操作符 在程序堆内存上为对象分配内存资源
     其它变量(包括指向对象的引用)占用栈内存,出栈操作自动释放
    不存在其它类型的内存占用情况,即使存在双方相互引用的对象gc算法也有解决之道
    finalize()的典型应用是在gc前给程序员个机会,来释放必要的资源。如数据库连接