6

As far as I know, a method's local variable is located in a stack frame in an executing thread and a reference type of a local variable only has a objects' reference, not the object itself. All of objects in JVM are located in a heap space.

I want to know that objects referenced by local variables in a method being executed are never garbage collected until the end of the method execution. (without using java.lang.ref.WeakReference and SoftReference.)

Are they garbage collected? or never? Is there compiler's optimization to this type of stuff?

(If they are never garbage collected, this means it may be needed to assign null to variables no longer used when executing big methods which take long time.)

ParkCheolu
  • 1,175
  • 2
  • 14
  • 30

2 Answers2

4

As elaborated in Can java finalize an object when it is still in scope?, local variables do not prevent the garbage collection of referenced objects. Or, as this answer puts it, scope is a only a language concept, irrelevant to the garbage collector.

I’ll cite the relevant part of the specification, JLS §12.6.1 again:

A reachable object is any object that can be accessed in any potential continuing computation from any live thread.

Further, I extended the answer’s example to

class A {
    static volatile boolean finalized;

    Object b = new Object() {
        @Override protected void finalize() {
            System.out.println(this + " was finalized!");
            finalized = true;
        }
        @Override public String toString() {
            return  "B@"+Integer.toHexString(hashCode());
        }
    };
    @Override protected void finalize() {
        System.out.println(this + " was finalized!");
    }

    @Override public String toString() {
        return super.toString() + " with "+b;
    }

    public static void main(String[] args) {
        A a = new A();
        System.out.println("Created " + a);
        for(int i = 0; !finalized; i++) {
            if (i % 1_000_000 == 0)
                System.gc();
        }
        System.out.println("finalized");
    }
}
Created A@59a6e353 with B@6aaa5eb0
B@6aaa5eb0 was finalized!
finalized
A@59a6e353 with B@6aaa5eb0 was finalized!

which demonstrates that even the method with the variable in scope may detect the finalization of the referenced object. Further, being referenced from a heap variable doesn’t necessarily prevent the garbage collection either, as the B object is unreachable, as no continuing computation can access it when the object containing the reference is unreachable too.


It’s worth emphasizing that even using the object does not always prevent its garbage collection. What matters, is whether the object’s memory is needed for the ongoing operation(s) and not every access to an object’s field in source code has to lead to an actual memory access at runtime. The specification states:

Optimizing transformations of a program can be designed that reduce the number of objects that are reachable to be less than those which would naively be considered reachable. […]

Another example of this occurs if the values in an object's fields are stored in registers. The program may then access the registers instead of the object, and never access the object again. This would imply that the object is garbage.

This is not only a theoretical option. As discussed in finalize() called on strongly reachable object in Java 8, it may even happen to objects while a method is invoked on them, or in other words, the this reference may get garbage collected while an instance method is still executing.

The only ways to prevent an objects garbage collection for sure, are synchronization on the object if the finalizer also does synchronization on the object or calling Reference.reachabilityFence(object), a method added in Java 9. The late addition of the fence method demonstrates the impact of the optimizers getting better from version to version on the issue of earlier-than-wanted garbage collection. Of course, the preferred solution is to write code that does not depend on the time of garbage collection at all.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • I don't see how this proves that an obect with a local-variable reference to it got collected. The finalization of A occurred *after* `main()` printed 'finalized', and as far as we can tell on this evidence after it exited too, i.e. when the `A` was out of scope, and collection (if any) after that. The finalization of the `B` member object is more interesting. – user207421 Jun 28 '20 at 03:54
  • 2
    @MarquisofLorne at the beginning of my answer is a link to [this answer](https://stackoverflow.com/a/24380219/2711488) which does already show how an object referenced by a local variable can get garbage collected. I only extended it to show that this allows in turn the collection of objects referenced by heap variables. Note that [JLS §12.6.1](https://docs.oracle.com/javase/specs/jls/se12/html/jls-12.html#jls-12.6.1-410) explicitly forbids the collection of B before A; both are collected together here, but since the order of finalization is unspecified, the JVM happens to finalize B before A. – Holger Jun 28 '20 at 09:59
0

It is not quite true that all of the objects are in heap space; but it is generally true. Java has been extended to have stack-local objects, provided the JVM can detect that the object will live only as long as the stack frame.

Now for the objects on the heap, which have a local reference in a method. While the method is being processed, the stack frame associated with the method run contains the local variable references. As long as the reference can be used (which includes being still in the stack frame) the object will not be garbage collected.

Once the reference has been destroyed, and the object can no longer be reached by the running program (because there's no references that can reach it), then the garbage collector will collect it.

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
  • Thanks. Which version of java has stack-local objects feature since? Is there any reference I can read? – ParkCheolu Mar 01 '19 at 03:34
  • 1
    The “stack-local objects” are a tenacious myth. JVMs like the HotSpot JVM have “object scalarization”, which means that the object gets *dissolved*; there will be no object header and the fields get converted into variables which are subject to subsequent optimizations, the same way as local variables, i.e. constant folding and elimination of redundant or unused variables. The remaining variables usually get mapped to CPU registers. So, some of the remaining fields *may* end up on the stack if there are not enough registers, but the result has no resemblance to the original heap object. – Holger Mar 01 '19 at 10:52
  • @Holger Thank you for pointing this out. I'm currently having a lot of fun reading up on escape analysis and how it has impacted the JVM (which so far seems to be very small). Without your commentary, I might have missed this opportunity to have a bit of learning fun this morning! – Edwin Buck Mar 01 '19 at 15:22
  • 1
    Mind that an object doesn’t need to be purely local to enable early garbage collection. E.g., a method may receive an object, held in reference variable `foo` and access `foo.x` multiple times. The optimized code may read `foo.x` only once and use the value of `x` from now on, without keeping a reference to `foo`. If `foo` was the only reference, the object may get garbage collected while `x` still is in use. Due to inlining, this may even apply if the use is indirectly, e.g. by invoking methods on `foo`. – Holger Mar 01 '19 at 16:01