【问题标题】:multiple call of the finalize多次调用 finalize
【发布时间】:2015-01-16 19:53:21
【问题描述】:

我尝试编写一个类来计算内存中的实例数。我使用了一个静态属性 - 我在构造函数中递增它并在 finalize 方法中递减它。

您可以在下面看到 Counter 类。

在主要课程 (TestCounter) 上,我列出了计数器。当我用小值(java TestCounter 10 100)测试这个类时,结果似乎很正常:

Size of the list : : 0
curently : 100 over 100 created
Size of the list : : 0
curently : 200 over 200 created
Size of the list : : 0
curently : 300 over 300 created
Size of the list : : 0
curently : 400 over 400 created
Size of the list : : 0
curently : 500 over 500 created
Size of the list : : 0
curently : 600 over 600 created
Size of the list : : 0
curently : 700 over 700 created
Size of the list : : 0
curently : 800 over 800 created
Size of the list : : 0
curently : 900 over 900 created
Size of the list : : 0
curently : 1000 over 1000 created
Still 1000 instances in memory... waiting ...
Still 1000 instances in memory... waiting ...
Still 1000 instances in memory... waiting ...
Still 1000 instances in memory... waiting ...

正如我在this question 的答案中读到的,垃圾收集器没有必要调用,所以我显式调用了 GC(3 条注释行)。现在几乎所有的实例似乎都解放了:

curently : 10000 over 10000 created
Still 7435 instances in memory... waiting ...
at the end : 0 over 10000 created

有时,1 或 2 个实例似乎留在内存中......

但如果我尝试使用更多对象(java TestCounter 10 100000),结果令人惊讶。 “实例数”可以为负数,提示finalize()方法被调用了两次……

Size of the list : : 0
curently : 896577 over 1000000 created
Still 892834 instances in memory... waiting ...
Still -762 instances in memory... waiting ...
Still -762 instances in memory... waiting ...
Still -762 instances in memory... waiting ... 

您能解释一下这种行为吗?

也许我犯了一个错误或误解了什么......

public class Counter{

    static long number = 0;
    static long totalNumber = 0;


    public Counter(){
        number++; 
        totalNumber++;

    }

    public static long getNumber(){
        return number;
    }

    public static long getNombreTotal(){
        return totalNumber;
    }

    protected void finalize() throws Throwable{
            try {       
                number--;
            } finally {
                super.finalize();
            }
    }

}

import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

public class TestCompteur{

    public static void methode(int iteration, int taille){

        for (int j=0; j<iteration; j++){

            ArrayList<Counter> liste = new ArrayList<Counter>();

            for (int i=0; i<taille; i++){
                liste.add(new Counter());
            }

            liste.clear();

            System.out.println("Size of the list : : "+liste.size());           
            System.out.println("curently : " + Counter.getNumber() + " over " + Counter.getNombreTotal() +" created");      
        }   



    }

    public static void main(String[] args){


        methode(Integer.decode(args[0]), Integer.decode(args[1]));

        /*
        System.gc();
        System.runFinalization();
        System.gc();
        */

        while (Counter.getNumber() != 0){
            //System.out.print(".");
            System.out.println("Still " + Counter.getNumber() + " instances in memory... waiting ...");

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(TestCompteur.class.getName()).log(Level.SEVERE, null, ex);
                    }
        }
        System.out.println("at the end : " + Counter.getNumber() + " over " + Counter.getNombreTotal() +" created");
    }

}

【问题讨论】:

  • 您的代码不是线程安全的。因此,number 的值可能与构造函数调用的数量减去 finalize 调用的数量不匹配。尝试在同步方法中修改number,或者将其类型更改为AtomicInteger,看看会发生什么。

标签: java garbage-collection finalize


【解决方案1】:

我试过你的代码。虽然我没有像你那样否定number,但我还是坚持了下来:

curently : 923367 over 1000000 created
Still 1041 instances in memory... waiting ...
Still 1041 instances in memory... waiting ...
Still 1041 instances in memory... waiting ...

然后我同步number的增量/减量:

public Counter()
{
    incNumber();
    totalNumber++;
}

public static synchronized void incNumber ()
{
    number++;
}

public static synchronized void decNumber ()
{
    number--;
}

protected void finalize() throws Throwable
{
    try {
        decNumber ();
    } finally {
        super.finalize();
    }
}

结果,number 达到 0,程序终止。

curently : 936759 over 1000000 created
at the end : 0 over 1000000 created

结论:

finalize() 不会为同一个对象调用两次,但是当多个线程(您的主线程和 GC 线程)在不同步的情况下修改同一个变量时,可能会发生奇怪的事情。

【讨论】:

  • 谢谢!!当然,我忘了并发访问的问题。
猜你喜欢
  • 1970-01-01
  • 2015-10-27
  • 2019-01-28
  • 2017-09-19
  • 2011-12-08
  • 1970-01-01
  • 2018-10-09
  • 2013-11-21
  • 2011-12-14
相关资源
最近更新 更多