2

On JDK 8, I ran the following code and found that it will finalize A every time. If i un-comment the println at the end, A will never be finalized.

public class A {
    @Override protected void finalize() {
        System.out.println(this + "object is eligible for garbage collection");
    }

    public static void main(String[] args) {
        A a = new A();
        System.out.println("Main thread created object " + a);
        for (int i = 0; i < 1000000000; i++) {
            if (i % 100000000 == 0)
                // Force GC
                System.gc();
        }
        //If i un-comment below it won't do garbage collection ever
       //  System.out.println(a + "is residing on heap and is alive");
    }
}

Can some one explain me this behavior, what happens after syso(), why it is not finalized then.

Sachin Sachdeva
  • 10,744
  • 2
  • 45
  • 108

2 Answers2

0

The garbage collection in last versions of JVM follows optimized principles to decide if a object is out of scope in a executed program.
It is rarely limited to the scope of the method.

From Oracle documentation :

What is Automatic Garbage Collection?

Automatic garbage collection is the process of looking at heap memory, identifying which objects are in use and which are not, and deleting the unused objects. An in use object, or a referenced object, means that some part of your program still maintains a pointer to that object. An unused object, or unreferenced object, is no longer referenced by any part of your program. So the memory used by an unreferenced object can be reclaimed.

The two last sentences matters :

An unused object, or unreferenced object, is no longer referenced by any part of your program. So the memory used by an unreferenced object can be reclaimed.

In your sample code :

public static void main(String[] args) {
    A a = new A();
    System.out.println("Main thread created object " + a);
    for (int i = 0; i < 1000000000; i++) {
        if (i % 100000000 == 0)
            System.gc();
    }
}

when System.gc(); is invoked, a is no longer referenced by any part of your program so a garbage collect request may mark it to be freed.

Now when you add System.out.println(a + "is residing on heap and is alive"); } , it may make sense that the finalizer is never called. It is not called when gc() is invoked as it is still referenced latter but the sysout is also the last line of the program.

The a instance is a instance of the main thread class (A).
If the main thread is finished, the current JVM process is down.
In these conditions, the finalize() method of it has probably no way to be executed.

Ps : sorry for multiple edits. I am on my phone...

Community
  • 1
  • 1
davidxxx
  • 115,998
  • 20
  • 192
  • 199
0

Without the additional System.out.println(a + "is residing on heap and is alive"); line, a is eligible for GC before the for loop. It is not in use by the main thread anymore, contrary to what your question says. Whether it is GC'd (and finalizers are run) is not something should care about. You can't/shouldn't write code that makes assumptions about GC behaviour.

With the commented out line, a doesn't become eligible for GC until afterwards, at which point it's too late as the JVM exits and at least in your case the finalizers are not run.

It's all normal behaviour, but as we know, you can't trust that the finalizers will or will not be run. You may experience different results in different environments.

Community
  • 1
  • 1
Kayaman
  • 70,361
  • 5
  • 75
  • 119
  • To add to this answer, finalizers also don't run in any particular order, and they add strongly to pressure on the Java heap because they severely retard GC of their objects. Time spent in finalizers​ is time not spent in domain logic. Finalizers are Java's nitroglycerin, useful but highly unstable and toxic, not meant to be used lightly. – Lew Bloch Apr 27 '17 at 07:11
  • If `foo` has a field `final int x`, could the JIT replace `System.out.println("x==" + x); ..slow computation..; System.out.println("x==" + x);` with `int temp=foo.x; System.out.println("x==" + temp); ..slow computation..; System.out.println("x==" + temp);`? Would it be required to keep `foo` alive if it did so? – supercat Apr 27 '17 at 16:11
  • @supercat That goes beyond my understanding of the JLS. I try not to write code that would get me into situations where I'd have to worry about that :) – Kayaman Apr 27 '17 at 16:18
  • @supercat repeated print statements usually prevent keeping the field value due to their internal `synchronized` blocks. But generally, optimizations using values of instance fields without keeping their owner alive are possible and responsible for [this neat bug](https://bugs.openjdk.java.net/browse/JDK-8145304) where the optimized code uses a wrapped thread pool directly, not needing the wrapper object anymore whose `finalize` method shuts down the thread pool. – Holger Nov 21 '19 at 14:29