Search code examples
javamemory-leaksgarbage-collectionfinal

How do final fields NOT leak memory?


I have been, without a question, using the final keyword for years to denote fields that should not change for the lifetime of an instance/class. Suddenly this occured to me...

So given this example:

public class TestFinalGC{

    private TestFinalGC(){}

    private final Object obj = new Object();

     public static void main(String []args){
         TestFinalGC instance = new TestFinalGC();
         // instance Ref -> actual instance ->? obj ref-> actual obj
         System.out.println(instance.obj);
         instance = null;
         //say GC makes sweep here... what happens?
         //lets assume theres more code, obj would obviously be eligible for GC on app exit.
     }
}

How does the obj member NOT leak here? Are final fields automatically WeakReferences such that if the strong references to parent(s) are nulled, they are eligible for garbage collection?

The JLS does not seem to note anything special about final

Update:

So this question of mine was founded on the premise that "reachability" and strong/weak references are closely related. There is this confusing oracle doc on reachability that leads me to believe that nested references should always be "strongly reachable". Hence, I do null my nested object references in all my objects, but it appears that this obviously should not be the case from all of the comments I am receiving.

So regarding "reachability", then, is it simply just that nested object references are no longer considered "reachable" if parent references are no longer reachable?

It could be is true that the premise of this problem is incorrect, but there is still intriguing information to consolidate here.


Solution

  • As Makoto suggested, there is simply nothing special about final in variable declarations as far as GC is concerned. In your example code

    private final Object obj = new Object();
    

    will be garbage collected at the same time as

    private Object obj = new Object();
    

    Both are strong references, but are invalidated and garbage collected together with their parent class TestFinalGC instance. That is because when the instance is GC'd, the reference fields are destroyed as well and the references do not exist any more. obj's reference count thus decreases by one.

    However, should you write something like

    Object x = myTestFinalGC.obj; // only works if your obj is not private, of course
    

    Then the object will not be garbage collected because it will still have one reference lingering around (assuming this particular line of code is in another class instance that remains alive when myTestFinalGC is garbage collected.

    tl;dr: memory allocations are garbage collected when their hard reference count drops to zero (and the collector runs, of course). final doesn't change this fact.