3. class Beta{ }
4. class Alpha {
5. static Beta b1;
6. Beta b2;
7. }
8. public class Tester {
9. public static void main(String[] args) {
10. Beta b1 = new Beta(); Beta b2 = new Beta();
11. Alpha a1 = new Alpha(); Alpha a2 = new Alpha();
12. a1.b1 = b1;
13. a1.b2 = b1;
14. a2.b2 = b2;
15. a1 = null; b1 = null; b2 = null;
16. // do stuff
17. }
18. }
I think 2 objects will be available for garbage collection
CodePudding user response:
The answer depends on the Java implementation ... and how you choose to interpret the question. In particular what precisely does line 16 mean?
- If we interpret line #16 as the Java compiler would interpret it, it is just a comment. It does nothing.
- The other interpretation is that line #16 is editorial "short hand" for some real Java code that may or may not use the value in
a2
, and may or may not use the value in the (static) variableAlpha.b1
.
According to the JLS for Java 6 (12.6.1):
"A reachable object is any object that can be accessed in any potential continuing computation from any live thread. 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. For example, a compiler or code generator may choose to set a variable or parameter that will no longer be used to null to cause the storage for such an object to be potentially reclaimable sooner."
So how can we apply that here?
If we treat line #16 as nothing more than a comment, then at that point in the main
method, the value of a2
can no longer influence any potentially continuing computation. But the compiler / optimizer / runtime may not be able to deduce that. In other words, the actual reachability status of a2
at line #16 is unclear.
Likewise, unless there is some other code in the application beyond what we have been shown, the value of Alpha.b1
also won't influence any potentially continuing computation. Indeed, since that variable is only ever written to, it could be optimized away entirely! But once again, we don't know know for sure whether the compiler / optimizer / runtime could or would deduce that Alpha.b1
is unreachable, though (IMO) it is much less likely that they would.
And there are a couple of additional factors:
- If the program is being debugged, that will affect the way the optimizer deals with the code and it may affect reachability. For example, setting a breakpoint in the
main
method is likely to inhibit any behind-the-scenes nulling ofa2
since the programmer may want to examine the variable. - It may not be obvious, but the reachability determination also depends on whether or not the
main
method has been optimized. For current generation JVMs that use HotSpot JIT compiler technology, it most likely won't be optimized in the first (and only) call. But with a AOT compiler, it might have been optimized.
Then there is the alternative interpretation of line #16 as a placeholder for some actual code. In that case, all bets are off as far as Alpha.b1
and a2
are concerned. We simply can't answer the question at all.
In summary, it is impossible give an unambiguous answer on how many objects remain reachable.
Note that when the code reaches line #16 (and it is just a comment), reachability is moot anyway. I can't think of anything that would cause the JVM to need to GC at that point ... or at any time up until the JVM process exits. (There are no shutdown hooks, and "finalization on exit" is no longer supported.)
CodePudding user response:
Let's give the objects allocated unambiguous names so we don't get confused with the variable and field names.
- The first allocated Beta - betaOne
- The second allocated Beta - betaTwo
- The first allocated Alpha - alphaOne
- The second allocated Alpha - alphaTwo.
In line 12 we assign betaOne to a static variable. This means that unless we clear that static variable, betaOne is not going to be eligible for garbage collection.
In line 14 we assign betaTwo to a non-static variable in alphaTwo. However, when we reach line 16, we do not clear alphaTwo, so its assigned variables are still reachable, so betaTwo is also still reachable.
So betaOne is reachable, and alphaTwo and betaTwo are reachable. Which leaves us onli with alphaOne not reachable.
So the answer is: 1.
CodePudding user response:
An object is eligible for garbage collection if it is unreachable. JLS, §12.6.1 defines unreachability:
Every object can be characterized by two attributes: it may be reachable, finalizer-reachable, or unreachable, and it may also be unfinalized, finalizable, or finalized.
A reachable object is any object that can be accessed in any potential continuing computation from any live thread.
A finalizer-reachable object can be reached from some finalizable object through some chain of references, but not from any live thread.
An unreachable object cannot be reached by either means.
Looking at the program at hand, we see that we create four object initially: a1
, a2
, b1
and b2
(lines 10 and 11);
We set a1.b1 = b1;
(line 12). This is a line to fool the unaware: we set the static field Alpha.a1
through an instance reference.
Then, we set a1.b2 = b1;
(line 13). Notice that we now set the instance field b2
of a1
.
On line 14, we set a2.b2 = b2
. Again, notice that we set the instance field b2
of a2
.
Finally, we null
all references except a2
.
For the following analysis, I am going to assume (urbandictionary.com
) that the comment on line 16 is a placeholder for a computation that accesses all variables that are alive (if we do not make this assumption, then objects that are referenced by unused variables are unreachable as per JLS, §12.6.1).
Now for the analysis of reachability:
a2
ist still reachable, so this object is not eligible for garbage collection.- Through
a2
, we cann still accessa2.b2
, so the object formerly referenceb by local variableb2
is not eligible - Through the class
Alpha
, we can still access the object formerly referenced byb1
(Alpha.b1
), so this instance is also not eligible - The only instance that is no longer reachable is the object formerly referenced by
a1
.
Thus, the final answer is: one object is eligible for garbage collection.
We can even observe1 this behaviour through PhantomReference
s:
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.concurrent.TimeUnit;
class Ideone {
public static void main(String[] args) throws InterruptedException {
Beta b1 = new Beta();
Beta b2 = new Beta();
Alpha a1 = new Alpha();
Alpha a2 = new Alpha();
a1.b1 = b1;
a1.b2 = b1;
a2.b2 = b2;
PhantomReference<Alpha> a1Phantom =
new PhantomReference<>(a1, new ReferenceQueue<>());
PhantomReference<Alpha> a2Phantom =
new PhantomReference<>(a2, new ReferenceQueue<>());
PhantomReference<Beta> b1Phantom =
new PhantomReference<>(b1, new ReferenceQueue<>());
PhantomReference<Beta> b2Phantom =
new PhantomReference<>(b2, new ReferenceQueue<>());
a1 = null;
b1 = null;
b2 = null;
System.gc();
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
System.out.printf(
"a1 has %sbeen collected%n",
a1Phantom.isEnqueued() ? "" : "not ");
System.out.printf(
"a2 has %sbeen collected%n",
a2Phantom.isEnqueued() ? "" : "not ");
System.out.printf(
"b1 has %sbeen collected%n",
b1Phantom.isEnqueued() ? "" : "not ");
System.out.printf(
"b2 has %sbeen collected%n",
b2Phantom.isEnqueued() ? "" : "not ");
}
}
class Beta {
}
class Alpha {
static Beta b1;
Beta b2;
}
Which prints:
a1 has been collected
a2 has not been collected
b1 has not been collected
b2 has not been collected
Showing that only a1
was collected.
A note on the code above:
In Java 16 , we would use a1.refersTo(null)
instead of a1.isEnqueued()
since method isEnqueued()
was deprecated, and method refersTo(T)
has been added.
1: The program relies on the garbage-collector collecting all objects eligible, which is not guaranteed. The garbage collector might not run at all, or it might not collect all eligible objects. This demo is merely meant to illustrate the behaviour, not as definitive proof. For the proof, please read the analysis.
CodePudding user response:
How many objects are eligible for garbage collection in the following question when line 16 is reached-----> 2