multiple call of the finalize

337 views Asked by At

I tried to write a class for counting the number of instances in memory. I used a static attribute - I increment it on the constructor and decrement it in the finalize method.

You can see the Counter class below.

On a main class (TestCounter), I made lists of counter. When I test this class with small values (java TestCounter 10 100) the result seems normal :

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 ...

As I read in an answer for this question, the garbage collector is not necessary called, so I call the GC explicitly (the 3 commented lines). And now nearly all instances seem liberated :

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

Sometimes, 1 or 2 instances seem stay to in memory...

But if I try with more objects (java TestCounter 10 100000), the results are surprising. The "number of instances" can be negative, suggesting that the finalize() method is called twice...

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 ... 

Could you explain this behavior?

Maybe I made an error or misunderstood something...

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");
    }

}
1

There are 1 answers

1
Eran On

I tried your code. While I didn't get negative number as you did, I got stuck on :

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

Then I synchronized the increments/decrements of 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();
    }
}

As a result, number reached 0 and the program terminated.

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

Conclusion:

finalize() is not called twice for the same object, but when multiple threads (your main thread and the GC thread) modify the same variable without synchronization, weird things can happen.