23
votes

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.

2
@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 documentation - Viachslau
@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. - Viachslau
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. - O.O
@O.O 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. - Viachslau
@O.O 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. - Viachslau

2 Answers

5
votes

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.

2
votes

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.