12

This is a question I was asked at my interview recently: Which 'Random' object(s) would get collected during the 'GC.Collect()' call?

String a = new Random().Next(0, 1) ==1 ? "Whatever 1" : "Whatever 2";

String b = new WeakReference(new Random()).Target.Next(0, 1) == 1 ?
    "Whatever 1" : "Whatever 2";

GC.Collect();

I answered that this is an implementation-specific question and it highly depends on the GC implementation and the corresponding weak reference semantics. As far as I know, C# specification doesn't provide exact description of what GC.Collect should do and how should the weak references be handled.

However, my interviewer wanted to hear something else.

Peter Olson
  • 139,199
  • 49
  • 202
  • 242
Yippie-Ki-Yay
  • 22,026
  • 26
  • 90
  • 148

4 Answers4

13

Both Random() instances and the WeakReference are eligible for collection:

  • The first Random was not stored in a local, let alone a local that is later read.
  • The second Random was passed to a WeakReference, so would be OK to collect anyway, but the WeakReference itself is not held anywhere, so that too is eligible for collection.

None of the strings are (there are only 2 string instances here, not 4, even if every possible code path was reached): because they are literals in the c# code, they are interned once they exist.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Yeah, obviously, the question was about the 'Random' objects, sorry, that I forgot to mention that. – Yippie-Ki-Yay Nov 19 '11 at 21:05
  • Now, can one say that my answer is incorrect and is the right answer that **both are?** – Yippie-Ki-Yay Nov 19 '11 at 21:06
  • 2
    Not true if the compiler puts them on the stack or local IL variables the runtime may still see references. The GC.Collect should happen outside the call site to be predictable. – Zarat Nov 19 '11 at 21:07
  • 1
    @Zarat the compiler won't invent locals without good reason; any part of the stack they are still in is opaque garbage and is treated as such – Marc Gravell Nov 19 '11 at 21:09
  • 3
    @MarcGravell - but that is an implementation detail. And the compiler does switch to locals when the stack starts getting too deep. Also you can never be sure which stack values are treated as garbage, remember that the GC sees JITted machine code not IL. – Zarat Nov 19 '11 at 21:14
  • @MarcGravell "the compiler won't invent locals without good reason". Actually that's exactly what a real compiler is likely to do. For example, LLVM's infinite register model turns every intermediate value into a new "variable". – J D Jan 06 '13 at 11:50
9

It is true that this is implementation (and compiler) dependant. If all this is in the same method you cannot know which objects are still on the stack. Since objects on the stack are still referenced they wouldn't be collectible.

What the interviewer wanted you most likely to do is to check which objects are still reachable at the call of GC.Collect assuming a "perfect" implementation which discards everything as soon as possible.

Zarat
  • 2,584
  • 22
  • 40
  • Why the downvote? This is important to know when coding unit tests involving GC behaviour, I'm speaking out of experience here. I explicitely made a difference between what the interviewer likely wanted to hear and what the technical reality is. – Zarat Nov 19 '11 at 21:19
  • +1 For being the first correct answer I've read. "assuming a perfect implementation". Given that the question is nonsense I would refrain from speculating about what the interviewer thought they were asking. – J D Jan 06 '13 at 11:40
3

However, my interviewer wanted to hear something else.

I expect they wanted to hear an answer like the one Marc Gravell has posted here but that is based upon an over-simplified model of how garbage collected virtual machines work that bears no resemblance to reality.

For example, the call to GC.Collect might get tail call optimized in which case there are no global roots so all heap-allocated blocks get collected. Or the compiler might create a new entry on the stack frame for every temporary (not just variables in the source code) which keeps everything reachable and nothing gets collected. Or the compiler might swap the order of the creation of the strings a and b and collect the Random object referred to by the WeakReference before the Target method is invoked causing a null reference exception so the other Random is never even allocated.

J D
  • 48,105
  • 13
  • 171
  • 274
  • What do you mean swap the strings, and how is that relevant to the `WeakReference`? – Nicholas W Jan 06 '13 at 12:31
  • I mean execute `new WeakReference(new Random()).Target.Next(0, 1) == 1 ? "Whatever 1" : "Whatever 2"` before executing `new Random().Next(0, 1) ==1 ? "Whatever 1" : "Whatever 2"`. – J D Jan 06 '13 at 12:40
  • Oh I missed half of that, the early collection of the `WeakReference` target + exception could happen in either order, it's just that it only affects the other `Random` if they are reordered. And in this case the compiler is free to reorder because 'nobody will ever know'. – Nicholas W Jan 06 '13 at 17:39
1

GC.Collect is like in Java equivalent to System.gc()

It "recommends" to check for null values to delete them. You can't really depend on this feature since it's really automatic unlike C++.

Hope it helps!

Martin Gemme
  • 345
  • 3
  • 17