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.

T-Bag
  • 10,916
  • 3
  • 54
  • 118
  • why do you mean by *every single time* – Scary Wombat Apr 27 '17 at 06:10
  • By every single time i mean 100% assurance of garbage collection – T-Bag Apr 27 '17 at 06:11
  • 5
    when you do `gc` `a` is no longer in use. If you un-comment `a` is still in use – Scary Wombat Apr 27 '17 at 06:12
  • But main thread holds the reference right, how could it be garbage collected – T-Bag Apr 27 '17 at 06:13
  • Maybe the JVM is smarter than us – Scary Wombat Apr 27 '17 at 06:14
  • Hahah...That's not the answer i am looking for dude... :) – T-Bag Apr 27 '17 at 06:14
  • 4
    @ShowStopper: No code path can ever use the value of `a` again, therefore it's logically eligible for garbage collection, IMO. The main JVM *used* to be more conservative here than the .NET CLR, but it looks like it's being aggressive now... – Jon Skeet Apr 27 '17 at 06:15
  • @JonSkeet- Sir, this is what we all think but i am looking for authentic answer which can clear the clouds as i had dig up JAVA docs but no reference regarding this is found. – T-Bag Apr 27 '17 at 06:23
  • 1
    @ShowStopper the relevant documentation is [in the language spec](https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.6). In particular, "The Java programming language does not specify how soon a finalizer will be invoked". – Andy Turner Apr 27 '17 at 06:59
  • @ShowStopper you've gotten the answer from a few people. The optimizer figured out the object wasn't used and released it. If you're looking for people to cite references I suggest that you ask the person in the mirror to do your work for you. You have the answer and presumably access to a search engine, yes? – Lew Bloch Apr 27 '17 at 07:06
  • @AndyTurner thanks for your inputs things are clear to me know – T-Bag Apr 27 '17 at 08:45
  • @JonSkeet--You are right as always... :) Thanks – T-Bag Apr 27 '17 at 08:58
  • @LewBloch-- i respect your views but i believe in "And Ye Shall Know the Truth and the Truth Shall Make You Free"... ;) i hope you understand. – T-Bag Apr 27 '17 at 09:20
  • @LewBloch- I looked into mirror and found the answer which i am looking for, please check my answer – T-Bag Apr 27 '17 at 13:13

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
  • 125,838
  • 23
  • 214
  • 215
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
  • 72,141
  • 5
  • 83
  • 121
  • 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