2
class CardBoard {
    Short story = 200;

    CardBoard go(CardBoard cb){
        cb = null;
        return cb;
    }

    public static void main(String[] args){
        CardBoard c1 = new CardBoard();
        CardBoard c2 = new CardBoard();
        CardBoard c3 = c1.go(c2);
        c1 = null;
// When this line is reached, what is eligible for the GC.
    }
}

Can somebody please explain what happens here? I understand that c1 and the Short are eligible for the Garbage Collector. But what happens with c2 and why isn't c3 also available for it? I might oversee something here but I would be glad for every input that helps me get around this topic. My OCA exam is next week and I still have trouble with this kind of stuff.

Edit: The comment of what is eligible for the GC.

Loskyll
  • 13
  • 5
  • Related: [Is Java “pass-by-reference” or “pass-by-value”?](https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value) – Lino Feb 24 '21 at 14:43
  • 1
    I hope my answer to your question made it more clear. I am also preparing for my OCA exam next week. So, if you have any other questions related to OCA; please ask them and I will try my best to answer them :D – Jens van Groeningen Feb 24 '21 at 15:40
  • Your question seems incomplete: You're talking about the eligibily for garbage collection at *which point in time*? – meriton Feb 24 '21 at 15:41
  • @meriton I changed it! – Loskyll Feb 24 '21 at 15:46
  • @JensvanGroeningen Thank you! I still have a lot of questions left tbh. But I will try to get my head around myself for now, if I have still open questions, I will ask you! Thank you for your answer and help :) – Loskyll Feb 24 '21 at 15:47
  • @JensvanGroeningen Btw. how do I open a chat? :D – Loskyll Feb 24 '21 at 16:23
  • 1
    Such questions are pointless, the garbage collector is there so the developer doesn’t need to reason about such questions, but even worse, the expected answer usually is fundamentally wrong. The formally correct answer would be that *all* the object visible in this code are eligible to garbage collection, because the subsequent code does not touch any of the objects. Worth reading: [Can java finalize an object when it is still in scope?](https://stackoverflow.com/q/24376768/2711488) – Holger Feb 24 '21 at 16:28
  • @JensvanGroeningen me neither.. :/ – Loskyll Feb 26 '21 at 09:30
  • @JensvanGroeningen Hey man! Sorry I can't answer on the chat because my Reputation is at 19 and you are allowed at 20 to participate in chats :D – Loskyll Mar 09 '21 at 18:00
  • Hey @Loskyll, I did my OCA exam last Thursday and passed with a score of 84% :D. How is your OCA certification going? – Jens van Groeningen Mar 15 '21 at 11:41
  • Hey @JensvanGroeningen! Congratulations man, that's an awesome score! My score wasn't that good, but at least it was enough to pass the exam! :D - Btw. I can't write in chat because I'm below those 20 points here to participate in Chat messaging! – Loskyll Mar 15 '21 at 14:04
  • @Loskyll Congrats! That’s great man! And who is ever gonna look at that score anyway :D (btw; I am still 100% sure that my answer to your question was the correct one instead of the one you marked as correct ) – Jens van Groeningen Mar 16 '21 at 17:55
  • @JensvanGroeningen Yeah we did it :D - and btw. I marked yours also as the correct answer. Just marked the other one because of the good illustration, not that it was 100% accurate but the illustration helped me a lot. Are you going to do OCP now or whats the plan? – Loskyll Mar 19 '21 at 09:37
  • @Loskyll thanks a lot! Do you mind if I remove this long conversation in your comments? Oh no, no plans for OCP yet, I had to get OCA certified for my employer. How about you? – Jens van Groeningen Mar 20 '21 at 15:31
  • @JensvanGroeningen I mean you could but why? Only option right now isnt it? :D. You are employed for Java? Lucky you, still looking for something, but also started 3 months ago. Doing preparation for OCP at the moment :) – Loskyll Mar 23 '21 at 12:26
  • @Loskyll Where are you from? – Jens van Groeningen Mar 24 '21 at 09:57
  • @JensvanGroeningen Germany. You? – Loskyll Mar 27 '21 at 19:43

2 Answers2

2

Considering Java is pass-by-value, we know that Java isn't passing the object, it's passing a reference to the object.

Because of this, when you did CardBoard c3 = c1.go(c2);, the method go(Cardboard cb) just copied the reference value of c2 to cb and therefore c2 is unharmed by the cb = null;. So, c2 is not eligible for GC.

c3 never gets to be constructed as an object, it is just a variable of type CardBoard initialized to null. Thus, it cannot be garbage collected because it is not even created on the heap.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
  • Ahh! Now I understand! I knew that c2 was never "harmed" since cb assigned itself to null but not taking the reference off of c2. Your last sentence makes it now clear for me. There is never an object for c3 created thus there is nothing to collect. Now it makes sense, thank you! – Loskyll Feb 24 '21 at 15:42
  • 1
    @Loskyll Glad I could help! For any other OCA related questions you can always send me a message! It will be a good exercise for both of us. – Jens van Groeningen Feb 24 '21 at 15:46
  • 1
    like any other question related to GC from `OCA`, this one is stupid. If that code is the _only_ code in the entire application, _everything_ is eligible for GC. – Eugene Feb 24 '21 at 16:26
  • @Eugene that may be but I am sure that this is the way of thinking they prefer for the OCA GC questions and thus making this the correct answer... – Jens van Groeningen Feb 24 '21 at 17:03
2

Perhaps visualizing the objects involved will help. After the first statement in main, we have:

         +-----------+        +-------+
         | CardBoard |   ---> | Short |
c1 ----> |-----------|  /     |-------|
         |  story    | /      |  200  |
         +-----------+        +-------+

After the second statement in main, we have:

         +-----------+        +-------+
         | CardBoard |   ---> | Short |
c1 ----> |-----------|  /     |-------|
         |  story    | /      |  200  |
         +-----------+        +-------+

         +-----------+        +-------+
         | CardBoard |   ---> | Short |
c2 ----> |-----------|  /     |-------|
         |  story    | /      |  200  |
         +-----------+        +-------+

(theoretically, it's also possible that the two CardBoard share the Short object, the Javadoc of Short.valueOf says:

This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.

However, the JDK implementation only caches in the range -128 to 127, so we are going to assume that going forward)

Upon entering the go() method, we have:

         +-----------+        +-------+
         | CardBoard |   ---> | Short |
c1 ----> |-----------|  /     |-------|
         |  story    | /      |  200  |
         +-----------+        +-------+

         +-----------+        +-------+
         | CardBoard |   ---> | Short |
c2 ----> |-----------|  /     |-------|
     --> |  story    | /      |  200  |
    /    +-----------+        +-------+
   / 
  /
cb

and before leaving go(), we have:

         +-----------+        +-------+
         | CardBoard |   ---> | Short |
c1 ----> |-----------|  /     |-------|
         |  story    | /      |  200  |
         +-----------+        +-------+

         +-----------+        +-------+
         | CardBoard |   ---> | Short |
c2 ----> |-----------|  /     |-------|
         |  story    | /      |  200  |
         +-----------+        +-------+
    
  
cb --x    

and after the 3rd statement in main, we have:

         +-----------+        +-------+
         | CardBoard |   ---> | Short |
c1 ----> |-----------|  /     |-------|
         |  story    | /      |  200  |
         +-----------+        +-------+

         +-----------+        +-------+
         | CardBoard |   ---> | Short |
c2 ----> |-----------|  /     |-------|
         |  story    | /      |  200  |
         +-----------+        +-------+
    

c3 --x

And before leaving main(), we have:

         +-----------+        +-------+
         | CardBoard |   ---> | Short |
c1 -x    |-----------|  /     |-------|
         |  story    | /      |  200  |
         +-----------+        +-------+

         +-----------+        +-------+
         | CardBoard |   ---> | Short |
c2 ----> |-----------|  /     |-------|
         |  story    | /      |  200  |
         +-----------+        +-------+
    

c3 -x    

As we can see, of the 4 objects allocated, 2 remain reachable, and 2 are eligible for collection.

meriton
  • 68,356
  • 14
  • 108
  • 175
  • if the code presented is the only code we should be aware of, everything is eligible for GC, since no one nowhere really uses any of the instances created. – Eugene Feb 24 '21 at 16:29
  • Awesome visualization, thank you for the effort! – Loskyll Feb 24 '21 at 16:29
  • 2
    If we go the route of pretending that local variables prevented garbage collection in general, i.e. ignore the contradicting, real life examples like in [this answer](https://stackoverflow.com/a/24380219/2711488), we should still not ignore the `c1 = null;` statement… – Holger Feb 24 '21 at 16:34
  • Added the last step. And of course, the JIT can perform further optimizations which can make an object eligble for collection early, or even allocate it on the stack instead. If `main()` were JIT optimized, for instance, escape analysis would determine that none of the objects allocated escape the stack frame, and allocate them all on the stack, so 0 objects would be eligible for collection. That is however far beyond the knowledge the Oracle Certification asks for - and in this case we can tell this won't happen because main() is obviously not a hotspot, and hence not optimized by JIT. – meriton Feb 24 '21 at 17:51
  • I like the visualization of this, I hate that `OCA` forces people to have a weird mindset about reachability. 1+, nevertheless – Eugene Feb 24 '21 at 19:40
  • 1
    “*this won't happen because main() is obviously not a hotspot*”—well garbage collection won’t happen at this point anyway, as there’s no memory allocation that could trigger it and the application is about to exit and release the heap memory as a whole. And it’s an implementation detail that the HotSpot JVM requires compilation to identify such formally unreachable objects. So the formally correct answer is, all objects are eligible to garbage collection at this point and the practical answer is, no object will get collected at this point. Neither matches what the exam wants to hear… – Holger Feb 25 '21 at 12:58
  • @Holger: That's why the exam merely asks which objects are *eligible* for collection, rather than which objects are actually collected. The exam therefore doesn't ask for speculation whether GC would run, it asks which objects can be collected if GC were to run at a particular point in time. – meriton Feb 25 '21 at 14:36
  • I agree that the OCA teaches a simplified model of memory management on the Java platform. Nevertheless, that simplified model is sufficient to prevent most rookie mistakes, and well suited to test whether people have understood the crucial difference between objects and references. This seems adequate given that the OCA is the lowest professional Java certification offered by Oracle. Teaching the intricacies of runtime optimizations and garbage collection algorithms at this point in time would wholly overwhelm newcomers. – meriton Feb 25 '21 at 14:44
  • 1
    @meriton “eligible” based on what? As said, according to the specification, all objects are eligible. If an implementation does not collect all objects, it’s due to implementation specific behavior, like some references being considered potentially reachable in interpreted mode. Or a pause time goal configured for a concurrent collector. Or… A question considering implementation specific restrictions would need to specify which implementation, configuration, current memory and time constraints, etc. Whatever combination you select, the real answer will never match that naïve “correct” answer. – Holger Feb 25 '21 at 14:49
  • I disagree that the spec mandates that all objects are eligible for collection here. In particular, while the spec says that the runtime "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", it doesn't require the runtime to do so. So while we are still in the main method, `c2` can keep the object alive, so that "correct answer" is actually possible. And it actually happens in interpreted mode. I verified this by inserting `System.gc()` and enabling GC logging. – meriton Feb 25 '21 at 16:44
  • 1
    @meriton this has been explained in [this answer](https://stackoverflow.com/a/24380219/2711488) already, the specification says: “*A* reachable *object is any object that can be accessed in any potential continuing computation from any live thread.*” The absence of references is an easy-to-check proof for no potential access, but the opposite does not hold, the presence of references does not imply reachability. Your “verification” is pointless as you changed the program. – Holger Feb 26 '21 at 07:40