Bug 57308 - GC.Collect doesn't collect (according to WeakReference)
Summary: GC.Collect doesn't collect (according to WeakReference)
Status: RESOLVED NOT_ON_ROADMAP
Alias: None
Product: Android
Classification: Xamarin
Component: Mono runtime / AOT Compiler ()
Version: unspecified
Hardware: PC Windows
: --- normal
Target Milestone: ---
Assignee: Rodrigo Kumpera
URL:
Depends on:
Blocks:
 
Reported: 2017-06-09 08:50 UTC by Miha Markic
Modified: 2017-06-21 18:36 UTC (History)
1 user (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
Repro (10.08 KB, application/x-rar)
2017-06-09 11:23 UTC, Miha Markic
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED NOT_ON_ROADMAP

Description Miha Markic 2017-06-09 08:50:33 UTC
Check out this code (on single view android template - hence the button.Text):
var tubo = new object();
var refx = new WeakReference(tubo);
tubo = null;
GC.Collect();
GC.WaitForPendingFinalizers();
button.Text = refx.IsAlive.ToString();

After running it, I'd expect button to show False. But instead it is showing True.
It doesn't help if I do more collections in a loop.
Comment 1 Marek Habersack 2017-06-09 11:20:11 UTC
@Miha, can you please create and attach a full, small, application that reproduces the issue as well as specify the Xamarin.Android version you are using and then reopen the bug, thanks!
Comment 2 Miha Markic 2017-06-09 11:23:23 UTC
Created attachment 22802 [details]
Repro
Comment 3 Miha Markic 2017-06-09 11:24:14 UTC
Sure, attached. Or you can simply create a new Single View Android app and paste the code into it. I'm running it on VS4A API 23 emulator.
Comment 4 Marek Habersack 2017-06-09 11:26:28 UTC
And what's your Xamarin.Android version?
Comment 5 Miha Markic 2017-06-09 11:29:04 UTC
VS 15.2 with all the updates (26430.12)
Xamarin 4.5.0.476
Xamarin.Android SDK 7.3.1.2
Comment 6 Marek Habersack 2017-06-09 11:32:53 UTC
Thanks!
Comment 7 Rodrigo Kumpera 2017-06-14 21:53:14 UTC
the runtime does conservative stack scanning, so this sort of behavior is expected.

This doesn't happen in actual app code, so it's not a practical concern.
Comment 8 Miha Markic 2017-06-15 06:37:03 UTC
Rodrigo, can you elaborate.

Why is the behavior expected? I mean the object is gone or not at that point?

"This doesn't happen in actual app code, so it's not a practical concern."
What do you mean by "in actual app code"? It is happening in actual app code. Or did you mean that it doesn't happen in release configuration?

Anyway, here is my scenario. I was unit testing some code and checking whether objects were released properly. Since WeakReference doesn't look working reliable at this point, how can I check it?
Comment 9 Rodrigo Kumpera 2017-06-15 07:41:04 UTC
The object, as you verified, is not gone.

When the object goes away is not deterministic and applications should not depend on it.

As you mentioned, you found the issue on an unit test that creates a scenario that doesn't exists on actual apps.

As how to test your code, assume WeakRefs work on normal setups, set them to null and check your invariants.
Comment 10 Miha Markic 2017-06-15 07:48:20 UTC
"When the object goes away is not deterministic and applications should not depend on it."

App doesn't depend on it. Just tests. But that is a sort of an app, isn't it :)

"As you mentioned, you found the issue on an unit test that creates a scenario that doesn't exists on actual apps."

OK, I'm still puzzled what exactly do you mean by "this doesn't exist on actual apps".

"As how to test your code, assume WeakRefs work on normal setups, set them to null and check your invariants."

Can you give me an example? Also what do you consider by a normal setup?
Comment 11 Rodrigo Kumpera 2017-06-21 18:36:19 UTC
In your unit test, you create the WeakRef, set a local variable to null then call GC all from the same method, on an actual app, that would be a really weird thing do to.

What usually happens is one method creates the WeakRef, store it somewhere and return. Then later the GC would trigger due to allocation and that WeakRef would be null'd.

The problem here is two-fold. First, our GC conservatively scans the stack and second, our compiler eliminates redundant stores such as the one that does "tubo = null;".

Conservative stack scanning means the GC looks at the contents of the execution stack and assume everything can potentially be an object reference. This causes objects to be wrongfully retained because we don't scrub the stack at the start or end of a method execution. Normal execution naturally erases old values from the stack.

In your example, tubo is saved on the stack as it's used as argument on a call.

Eliminating redundant stores is a common optimization as they don't change program behavior.