Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[windows] fix memory leak in WebView #18810

Merged
merged 1 commit into from
Nov 20, 2023

Conversation

jonathanpeppers
Copy link
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 🪟 memory-leak 💦 Memory usage grows / objects live forever 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
@@ -25,7 +26,7 @@ internal WebNavigationEvent CurrentNavigationEvent

protected override void ConnectHandler(WebView2 platformView)
{
platformView.CoreWebView2Initialized += OnCoreWebView2Initialized;
_proxy.Connect(this, platformView);
Copy link
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
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
Member

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
47 checks passed
@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! memory-leak 💦 Memory usage grows / objects live forever platform/windows 🪟
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants