Skip to content

[windows] fix memory leak in WebView#18810

Merged
jonathanpeppers merged 1 commit intodotnet:mainfrom
jonathanpeppers:WebViewLeaks
Nov 20, 2023
Merged

[windows] fix memory leak in WebView#18810
jonathanpeppers merged 1 commit intodotnet:mainfrom
jonathanpeppers:WebViewLeaks

Conversation

@jonathanpeppers
Copy link
Copy Markdown
Member

Context: #18365
Context: https://github.com/heyThorsten/GCTest

Testing the sample app, I could see a leak in WebView on Windows. A memory snapshot showed:

Microsoft.Maui.Handlers.WebViewHandler
    Windows.Foundation.TypedEventHandler<Microsoft.UI.Xaml.Controls.WebView2, Microsoft.UI.Xaml.Controls.CoreWebView2InitializedEventArgs>
        WinRT._EventSource_global__Windows_Foundation_TypedEventHandler_global__Microsoft_UI_Xaml_Controls_WebView2__global__Microsoft_UI_Xaml_Controls_CoreWebView2InitializedEventArgs_+EventState
            Windows.Foundation.TypedEventHandler<Microsoft.UI.Xaml.Controls.WebView2, Microsoft.UI.Xaml.Controls.CoreWebView2InitializedEventArgs> [Dependent Handle, Microsoft.Maui.Platform.LayoutPanel <0x2079E7A8D00>]
    Microsoft.UI.Xaml.RoutedEventHandler
        WinRT._EventSource_global__Microsoft_UI_Xaml_RoutedEventHandler+EventState
            Microsoft.UI.Xaml.RoutedEventHandler [Dependent Handle, Microsoft.Maui.Platform.LayoutPanel <0x2079E7A8D00>]

I could this same leak in WebView by updating MemoryTests to do:

if (view is WebView webView)
{
    webView.Source = new HtmlWebViewSource { Html = "<p>hi</p>" };
    await Task.Delay(1000);
}

After a bit of debugging, I found that events on WebView seem to suffer from the same "circular reference" problem we see on iOS. This makes sense, as it is an unmanaged/native control. (Might be COM?)

So we have the cycle:

  • WebViewHandler ->
  • WebView2 ->
  • Various WebView2 events:
    • CoreWebView2Initialized
    • HistoryChanged
    • NavigationStarting
    • NavigationCompleted
  • WebViewHandler

Using the same pattern we've been using for iOS: creating a WebView2Proxy type solves the issue. Note that I didn't have to do this for all events, just the ones subscribing to WebView2-related events. I also don't see the WebView2Proxy object living forever, so the issue does appear to be a cycle.

Context: dotnet#18365
Context: https://github.com/heyThorsten/GCTest

Testing the sample app, I could see a leak in `WebView` on Windows. A
memory snapshot showed:

    Microsoft.Maui.Handlers.WebViewHandler
        Windows.Foundation.TypedEventHandler<Microsoft.UI.Xaml.Controls.WebView2, Microsoft.UI.Xaml.Controls.CoreWebView2InitializedEventArgs>
            WinRT._EventSource_global__Windows_Foundation_TypedEventHandler_global__Microsoft_UI_Xaml_Controls_WebView2__global__Microsoft_UI_Xaml_Controls_CoreWebView2InitializedEventArgs_+EventState
                Windows.Foundation.TypedEventHandler<Microsoft.UI.Xaml.Controls.WebView2, Microsoft.UI.Xaml.Controls.CoreWebView2InitializedEventArgs> [Dependent Handle, Microsoft.Maui.Platform.LayoutPanel <0x2079E7A8D00>]
        Microsoft.UI.Xaml.RoutedEventHandler
            WinRT._EventSource_global__Microsoft_UI_Xaml_RoutedEventHandler+EventState
                Microsoft.UI.Xaml.RoutedEventHandler [Dependent Handle, Microsoft.Maui.Platform.LayoutPanel <0x2079E7A8D00>]

I could this same leak in `WebView` by updating `MemoryTests` to do:

    if (view is WebView webView)
    {
        webView.Source = new HtmlWebViewSource { Html = "<p>hi</p>" };
        await Task.Delay(1000);
    }

After a bit of debugging, I found that events on `WebView` seem to
suffer from the same "circular reference" problem we see on iOS. This
makes sense, as it is an unmanaged/native control. (Might be COM?)

So we have the cycle:

* `WebViewHandler` ->
* `WebView2` ->
* Various `WebView2` events:
    * `CoreWebView2Initialized`
    * `HistoryChanged`
    * `NavigationStarting`
    * `NavigationCompleted`
* `WebViewHandler`

Using the same pattern we've been using for iOS: creating a
`WebView2Proxy` type solves the issue. Note that I didn't have to do
this for all events, just the ones subscribing to `WebView2`-related
events. I also don't see the `WebView2Proxy` object living forever, so
the issue does appear to be a cycle.
@jonathanpeppers jonathanpeppers added platform/windows perf/memory-leak 💦 Memory usage grows / objects live forever (sub: perf) labels Nov 16, 2023
@jonathanpeppers jonathanpeppers marked this pull request as ready for review November 16, 2023 19:41
@jonathanpeppers jonathanpeppers requested a review from a team as a code owner November 16, 2023 19:41
@ghost ghost added the legacy-area-controls Label, Button, CheckBox, Slider, Stepper, Switch, Picker, Entry, Editor label Nov 16, 2023
@jonathanpeppers jonathanpeppers added this to the .NET 8 + Servicing milestone Nov 16, 2023
protected override void ConnectHandler(WebView2 platformView)
{
platformView.CoreWebView2Initialized += OnCoreWebView2Initialized;
_proxy.Connect(this, platformView);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hum shouldn't we just connect the proxy when WebView2 is initialized? I think we need this I remember other bug related with this where there was an exception related with this. @Eilon do you remember ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The connect method should be the same as what it was doing before:

			public void Connect(WebViewHandler handler, WebView2 platformView)
			{
				_handler = new(handler);
				platformView.CoreWebView2Initialized += OnCoreWebView2Initialized;
			}

I don't think I changed any events -- just moved methods to a new class.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@rmarinho hmm sorry I don't specifically remember a bug around this, but maybe there was.

@jonathanpeppers jonathanpeppers enabled auto-merge (squash) November 18, 2023 06:46
@jonathanpeppers jonathanpeppers merged commit 4cb5678 into dotnet:main Nov 20, 2023
@github-actions github-actions bot locked and limited conversation to collaborators Dec 21, 2023
@Eilon Eilon removed the legacy-area-controls Label, Button, CheckBox, Slider, Stepper, Switch, Picker, Entry, Editor label May 10, 2024
@samhouts samhouts added the fixed-in-8.0.6 Look for this fix in 8.0.6 SR1! label Aug 2, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-controls-webview WebView fixed-in-8.0.6 Look for this fix in 8.0.6 SR1! perf/memory-leak 💦 Memory usage grows / objects live forever (sub: perf) platform/windows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants