UWP compiled binding x:Bind produces memory leaks
Asked Answered
C

2

23

While developing UWP application I recently found quite a few memory leaks preventing my pages from being collected by GC. I have a ContentPresenter on my page like:

<ContentControl Grid.Column="0" Grid.Row="1" Content="{x:Bind ViewModel.Schedule, Mode=OneWay}">
</ContentControl>

After I remove the Content, or replace it with dynamic {Binding} -- page is collected when I navigate from it. Otherwise it remains in memory. Is it bug or I'm doing something wrong? Is there a way to release and clear ALL the bindings on navigating from?

UPDATE: It seems to be a known problem inside Microsoft as was stated here. But as far as I can see through my own test/app usage the data that is preserved by the x:Bind still gets collected after some time, when you, for example, navigate to the same pages or create the same controls for some time. I could see that new objects were created, but old ones at some point got collected.

So for me it doesn't seem to be a drastic problem causing out of memory, it only doesn't allow objects get collected as quickly as dynamic binding will.

Conflagration answered 15/9, 2015 at 8:36 Comment(11)
@O.O Thanks for answer! I should only correct, that for compiled bindings default Mode is OneTime, so I have to specify OneWay in this case. link to documentationConflagration
@O.O concerning memory leaks I've seen the following behavior in profiler: if I have dynamic bindings -- objects are collected on each Snapshot after I navigate from page. With compiled bindings -- they are not, BUT at some moment the DO get collected, while some of them are still preserved.Conflagration
Thanks for the correction. I have the same problem in my app, but with dynamic bindings and have not been able to isolate the issue other than I notice navigating back and forth exacerbates the issue. You could try using the OnNavigatedTo/From event to clear the datacontext and databindings and navigation stack. If it works in your scenario, you could also enable page caching.Birch
@Birch actually I've already added DataContext null set on Unloaded event, but it doesn't clean compiled bindings. The thing I personally use in mu apps is cleaning forward stack completely when going back. I have never ever found it useful to go forward so I simply wipe it.Conflagration
@Birch concerning caching, I have a situation, where user navigates to the same Page type with different params. And the problem with caching is that the page never recreates, it's the same instance, which prevents navigation transition, nice back navigation and other. It simply rewrites the content. I don't no how to make it cache the instance, but create the new one while navigating.Conflagration
Seems like you've tried a lot, I'm going to wait for the answer on this one too.Birch
what is 'ViewModel.Schedule', and do you have a strongly-typed datatemplate for that type that gets instantiated inside the contentpresenter ?Sutter
@DeanChalk what do you mean, what is "Schedule"? Let's say it is simply a model object with properties to access data. I do have a strongly-type datatemplate that I use inside so I precisely know which properties I can access.Conflagration
What happens if you replace ContentControl with a ContentPresenter?Cognomen
@Musters thanks for suggestions, but I can't use it in my page. ContentPresenters are meant to be used inside ContentControls templates to place the content. The problem isn't with the ContentControl itself.Conflagration
using Bindings.StopTracking() with the Unloaded event seems to work, but how do we go with compiled bindings inside a DataTemplate in a ResourceDictionary? There's no Unloaded event for it.Clamatorial
T
6

I have created bug on Microsoft connect because of this issue.

https://connect.microsoft.com/VisualStudio/feedback/details/3077894/memory-leaks-in-c-uwp-apps-using-compiled-x-bind-bindings

Work around for this issue is to explicitly call Bindings.StopTracking() at page Unloaded event handler. It's because compiled bindings doesn't use "weak event" pattern and does subscribe to PropertyChanged event of INotifyPropertyChanged directly. It's a cause of memory leak. To unsubscribe events you can call Bindings.StopTracking(). Compiled bindings code doesn't call it automatically.

Teodoro answered 23/8, 2016 at 11:8 Comment(1)
Work around for this issue is to explicitly call Bindings.StopTracking() at page Unloaded event handler. It's because compiled bindings doesn't use "weak event" pattern and does subscribe to PropertyChanged event of INotifyPropertyChanged directly. It's a cause of memory leak. To unsubscribe events you can call Bindings.StopTracking(). Compiled bindings code doesn't call it automatically.Teodoro
W
2

Yes it does cause memory leak, to prevent that you can use following steps:

  1. Use IoC like UnityContainer and make your ViewModel or View ContainerControlLifeTime
  2. Assign null to ViewModel property at xaml.cs once you move out of UI.
Wyon answered 18/3, 2016 at 11:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.