在多个窗口中使用 WPF WebBrowser 控件时发生内存泄漏

Posted

技术标签:

【中文标题】在多个窗口中使用 WPF WebBrowser 控件时发生内存泄漏【英文标题】:Memory leak when using WPF WebBrowser control in multiple windows 【发布时间】:2011-01-05 08:58:29 【问题描述】:

我正在开发一个使用 WPF WebBrowser 控件 (System.Windows.Controls.WebBrowser) 的项目。该程序的网络浏览器元素是用户可以参与的众多活动之一,并在单独的窗口中打开。用户离开浏览器后,窗口关闭,每次用户返回浏览器时都会创建一个新窗口。在持续使用浏览器时,我们注意到程序中出现了显着的内存泄漏/性能下降(使用量从最初的 ~200 增加到 ~700mb)。在我们自己的代码中找不到任何资源泄漏点后,我决定确定问题出在我们自己的 WebBrowser 包装控件还是 WPF 控件。

我创建了一个新的简单项目,其中仅包含一个 MainWindow 和一个 WebWindow。主窗口上的一个按钮会启动一个指向 gmail 的浏览器(在我们检查的少数网站中,我们注意到最大的问题)。关闭此窗口后,不会释放资源(任务管理器或进程资源管理器中的 VM 大小不会减少)并且进程处理的 GDI 对象的数量不会减少(程序从 ~30 开始,打开浏览器需要它到 ~140 并且在关闭浏览器后 ~140 仍然打开)。打开另一个浏览器会导致更多的句柄和更多的资源被分配。此外,通过专门调用 WebBrowser 控件上的 Dispose() 并不能解决此问题。代码很简单,如下:

主窗口:

<Window x:Class="WebBrowserMemory.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
            <Button Click="Button_Click">Gmail</Button>
        </StackPanel>
    </Grid>
</Window>

Button_Click:

private void Button_Click(object sender, RoutedEventArgs e)

    var win = new WebWindow();
    win.Show();
    win.Browser.Navigate("http://www.gmail.com");

网页窗口:

<Window x:Class="WebBrowserMemory.WebWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WebWindow" Height="300" Width="300">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <WebBrowser Grid.Row="0" x:Name="_browser" />
    <Button Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="10" Padding="10" Click="Button_Click">Close</Button>
</Grid>
</Window>

相关代码:

public WebBrowser Browser 
    get  return _browser; 


private void Button_Click(object sender, RoutedEventArgs e)

    Close();


protected override void OnClosed(EventArgs e)
        
            _browser.Dispose();
            base.OnClosed(e);
        

还有其他人在使用 WPF WebBrowser 控件时遇到过这个问题吗?

[更新:根据 itowlson 的回答更新帖子以指示 Dispose() 调用 - 即使在 Web 浏览器控件上调用 Dispose() 也不会释放资源]

【问题讨论】:

据我所知,它仍然存在。我开始使用 Gecko 包装器,并且正在考虑将来用 Awesomium 替换该组件 我遇到了同样的问题,但仍然找不到解决方案。有什么想法吗? 见this SO post - I found a solution to this - turns out it was a rogue JS plugin。 【参考方案1】:

我无法完全解决泄漏,但是,我注意到在处理之前将浏览器导航到“about:blank”肯定有助于减少闲置的内存量。

【讨论】:

【参考方案2】:

我们改用了 WinForm WebBrowser 控件,它是在 WPF 中的 FormsHost 中创建的,但是从 UI 的角度来看两者的工作方式几乎相同,但我们发现 WinForms 的 WebBrowser 与 WPF 中的 WebBrowser 相比具有更好的功能和更好的性能.

您可以手动处理 WinForm 控件的 WebBrowser,它肯定会相应地处理其所有子控件和释放资源,但是根据我过去的经验,WinForm 的 WebBrowser 在关闭后不会释放 100% 的资源,但是是的,这要好得多WPF。

【讨论】:

【参考方案3】:

与大多数 WPF 控件不同,WebBrowser(因为它继承自 HwndHost)是 IDisposable 并封装了非托管资源。 WPF 窗口与 WinForms 窗体不同,它不会自动释放其子级(因为本机 WPF 控件不封装非托管资源并且不需要释放)。

将 OnClosed 覆盖添加到您的窗口(或处理 Closed 事件),并在 WebBrowser 控件上调用 Dispose。

【讨论】:

抱歉,忘记了 Dispose,我正在处理的实际项目确实调用了 Dispose 方法,我只是忘记将它添加到我的演示应用程序中。不幸的是,显式调用 Dispose 并不能解决问题。少数 GDI 句柄已发布,但绝大多数保持打开状态。更新了我的帖子以表明 Dispose() 被明确调用

以上是关于在多个窗口中使用 WPF WebBrowser 控件时发生内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

StackPanel中的WebBrowser不会在WPF中扩展到其内容大小

WPF:如何在弹出窗口中自动调整 WebBrowser 的大小?

wpf中我想在WebBrowser控件上放置一个Border控件

如何在 webbrowser WPF 顶部打开用户控件作为弹出窗口

有没有办法在 wpf WebBrowser 控件之上呈现 WPF 控件?

WPF c# webbrowser 在顶部菜单上滚动