Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Fix for BindingExpression memory leak #279

Merged
merged 2 commits into from
Aug 16, 2016

Conversation

jonathanpeppers
Copy link
Member

@jonathanpeppers jonathanpeppers commented Aug 4, 2016

Description of Change

In our application we discovered a memory leak in the following situation:

  • Have a long-lived ViewModel object (maybe lives the lifetime of your app)
  • Bind to it in a View
  • The View goes away, and get's GC'd
  • A BindingExpression object and its children remain in memory as long as the long-lived ViewModel

Bugs Fixed

  • Not sure if there is an issue out for this, see my unit test that reproduces the issue

API Changes

None

Behavioral Changes

BindingExpression will not remain in memory when the View is GC'd.

PR Checklist

  • Has tests (if omitted, state reason in description)
  • Rebased on top of master at time of PR
  • Changes adhere to coding standard
  • Consolidate commits as makes sense

Jonathan Peppers added 2 commits August 4, 2016 10:30
What we were seeing in our app was that Binding objects stay around when
bound to long-lived ViewModels, even when the View is long gone
I had to make a WeakPropertyChangedProxy class for this, I could not
think of a way to get around creating a new object for this
@dnfclas
Copy link

dnfclas commented Aug 4, 2016

Hi @jonathanpeppers, I'm your friendly neighborhood .NET Foundation Pull Request Bot (You can call me DNFBOT). Thanks for your contribution!
You've already signed the contribution license agreement. Thanks!

The agreement was validated by .NET Foundation and real humans are currently evaluating your PR.

TTYL, DNFBOT;

@@ -410,11 +406,57 @@ public BindingPair(BindingExpressionPart part, object source, bool isLast)
public object Source { get; private set; }
}

class WeakPropertyChangedProxy
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this, couldn't you reuse the https://github.com/xamarin/Xamarin.Forms/blob/master/Xamarin.Forms.Core/WeakEventManager.cs ?

I'm uneasy about having multiple classes doing very similar stuffs.

@StephaneDelcroix
Copy link
Member

Nasty one...

I can reproduce the bug, and the fix solves it. I'd like to get rid, if possible, of the WeakPropertyChangedProxy. @hartez could you look at this with @jonathanpeppers ?

Once this is solved, you'll get my 👍 , until then, you'll have to do with my :<3:

@jonathanpeppers
Copy link
Member Author

@StephaneDelcroix @hartez

I did a quick attempt at changing this, but it seems like WeakEventManager has to be used by the class that is sending the event. Binding and its inner workings are the subscribers to any class implementing INotifyPropertyChanged. A developer could be binding any POCO here, as long as it was INotifyPropertyChanged.

I patterned WeakPropertyChangedProxy after WeakNotifyProxy I found inside ListView, and added an option to unsubscribe: https://github.com/xamarin/Xamarin.Forms/blob/master/Xamarin.Forms.Core/ListProxy.cs#L361

Let me know if I'm missing something here. I did feel like I was adding a class that might already exist.

@hartez
Copy link
Contributor

hartez commented Aug 15, 2016

@StephaneDelcroix @jonathanpeppers Jonathan's right, there's no simple way to use one in place of the other; WeakEventManager is tightly coupled to its event source, and WeakPropertyChangedProxy is tightly coupled to INotifyPropertyChanged (as WeakNotifyProxy in ListView is to INotifyCollectionChanged). They're all really doing different things.

There might be a way to consolidate everything they do into one class, but I suspect we'd have to do a lot of reflection to get the event handlers attached and I don't know that it'd be worth the performance hit.

@jassmith jassmith merged commit f6febd4 into xamarin:master Aug 16, 2016
@jonathanpeppers jonathanpeppers deleted the fix-binding-leak branch August 31, 2016 13:21
@jamesl77
Copy link

jamesl77 commented Oct 20, 2016

This is a serious bug. Has this been released in any NuGet package?
I can't find it in 2.3.3.152-pre2 nor in 2.3.3.163-pre3....
I'm confused, this is a serious bug which was fixed...

@jamesl77
Copy link

This memory leak bug was fixed 1.5 months ago, when do you plan to release it?

@samhouts
Copy link
Member

samhouts commented Oct 20, 2016

It was released in 2.3.2-pre2. It was just missed in the release notes.

@jamesl77
Copy link

@samhouts thanks. Looking at the commits, I see this one actually got into beta-2.3.3-pre1, am I correct?
I'm trying to make sure I understand the commits correctly.

@samhouts
Copy link
Member

I misspoke and updated my comment. It was actually released in 2.3.2-pre2 and has been in each subsequent build.

@jamesl77
Copy link

On https://github.com/xamarin/Xamarin.Forms/commits/beta-2.3.3-pre1 I see it shows all commits up to September 1. Jason merged this PR on August 16.
Doesn't this mean it got into beta-2.3.3-pre1?

@samhouts
Copy link
Member

You'll see it is also here: https://github.com/xamarin/Xamarin.Forms/commits/beta-2.3.2-pre2

@jamesl77
Copy link

jamesl77 commented Oct 20, 2016

Oh, the first NuGet prerelease for 2.3.3 is 2.3.3.152-pre2 on Sept 15... There was no 2.3.3 pre1 NuGet release

@jamesl77
Copy link

jamesl77 commented Oct 20, 2016

This kind of bugs are so hard to spot...Thank you @jonathanpeppers for this PR.... I wonder what other mem leak bugs are out there...

@jamesl77
Copy link

jamesl77 commented Oct 20, 2016

@jonathanpeppers how did you spot this memory leak by the way? Did you use any tools?
Does anyone know? It might be useful for anyone who gets into issues...

@jonathanpeppers
Copy link
Member Author

jonathanpeppers commented Oct 20, 2016

@jamesl77 we have some heavy screens with a lot of bindings. One in particular we could navigate to, dismiss, then it would get slower and slower each time.

To figure it out, I broke portions of our code into unit tests of this pattern:

WeakReference weakRef;

{
  //Do stuff that might cause a leak

  //Assign weakRef
  weakRef = new WeakReference(yourObject);
}

GC.Collect();
await Task.Delay(10);
GC.Collect();

Assert.IsFalse(bindingRef.IsAlive, "This thingy should not be alive!");

After I fixed all the leaks that were our fault (related to events), I discovered I could recreate the issue in Forms with this test.

After the leak was fixed, I used Heapshot/Xamarin profiler to make sure I really fixed it.

@jamesl77
Copy link

jamesl77 commented Oct 20, 2016

Thank you. I'm also using a lot of bindings and other stuff.

I have an issue with Messenger, I made a question on SO: http://stackoverflow.com/questions/40163598/does-messenger-really-requires-calling-unscribe

@samhouts samhouts added this to the 2.3.3 milestone Jun 27, 2018
mattleibow pushed a commit that referenced this pull request Jan 28, 2021
* Update Support Packages, Forms, and Reference Vectors

* Bump targetsdk

* Fix android build :)

* Fix build
mattleibow pushed a commit that referenced this pull request Jan 28, 2021
* GH-221: Add iOS SecAccessible properties (#223)

* Address #221, use AfterFirstUnlockThisDeviceOnly to match other platforms. Although allow a platform specific override to set accessible state.

* Add tests

* Added iOS specific prop to set SecAccessible

* Default to AfterFirstUnlock on iOS SecureStorage

* A few fixes for the release. (#228)

* Setting the correct flags for Chrome Tabs. Fixes #225

* Make sure that there is data to decrypt before starting. Fixes #226

* Fixes for TTS. Fixes #227

* A bit of the fixes for emails on iOS. Relates to #224

* Update the email bit for iOS #224

* Cleaning up the code #224

* Make sure to set the email properties. Fixes #229

* Update SDK Extras

This fixes a lot of references that were required on android that aren't.

* Update Readme with Installation information. (#237)

* Update Readme with Installation information.

* Update README.md

* Update README.md

* Adding the initial work to get Tizen started. #23

* Revert "Adding the initial work to get Tizen started. #23"

This reverts commit 58b6041.

* Update the docs with the latest APIs (#233)

Add docs for SecAccessible

* GH-245: Add Orientation Sensor (#249)

* Add Orientation Sensor

* Add Orientation Sensor

* Remove .csproj clutter

* "orientationsensor" --> "orientation sensor" or just "orientation"

* GH-192: Added DateTime to Preferences (#232)

* Added DateTime to preferences. Fixes #192
 - also properly using overloads

* Added some unit tests

* Added the docs for the new Preferences APIs

* Update Readme with Device Display Information

Added link to Current Features for Device Display Information

* Fixes #258 and Fixes #255 (#259)

* Fixes #258
* put vectors nuget everywhere.
* NuGet is broken - https://developercommunity.visualstudio.com/content/problem/232996/warning-nu1603-runtimenativesystemiocompression-41.html
* The NuGet doesn't add the assembly references from the GAC
* We need to use a later version of the iOS SDK

* Fix typo (#274)

* Fix up null checks when getting lask known location.

* Additional Null checks

* GH-240 Add IsMainThread detection API (#277)

* Add IsMainThread detection API

* Add platform tests for main thread

* Update docs and add more platform tests

* iOS secure storage simulator tests (#247) (#278)

* iOS secure storage simulator tests (#247)

* Remove line that skips test on virtual devices

* Adding keychain-access-groups in Entitlements.plist

* Setting CodeSign entitlements for Debug configuration

* Making same entitlement.plist changes for iOS sample

* Removing specific code sign key

* Removing physical device trait for iOS SecureStorageTests

* Update DeviceTests.iOS.csproj

* Update Samples.iOS.csproj

* Add configs for codesign on release

* Fixes #181 Allowing setting of calibration for heading. (#282)

* GH-254 Update Support Packages, Forms, and Reference Vectors (#279)

* Update Support Packages, Forms, and Reference Vectors

* Bump targetsdk

* Fix android build :)

* Fix build
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants