Утечка памяти при использовании элемента управления WPF WebBrowser в нескольких окнах

Я работаю над проектом, который использует элемент управления WPF WebBrowser (System.Windows.Controls.WebBrowser). Элемент веб-браузера программы является одним из многих видов деятельности, которые пользователь может задействовать и открывается в отдельном окне. После того, как пользователь переходит от браузера, окно закрывается, и каждое окно создается каждый раз, когда пользователь возвращается в браузер. Мы заметили значительную утечку памяти/снижение производительности в нашей программе (использование до ~ 700 мб от ~ 200 начальных) при постоянном использовании браузера. Не сумев найти какие-либо точки утечки ресурсов в нашем собственном коде, я решил определить, была ли проблема с нашим собственным элементом управления WebBrowser или элементом управления WPF.

Я создал новый простой проект, состоящий только из MainWindow и WebWindow. Кнопка в главном окне запускает браузер, направленный на gmail (сайт, на который мы обратили внимание, самая большая проблема с немногими, которые мы рассмотрели). После закрытия этого окна освобождение ресурсов (без уменьшения размера виртуальной машины в диспетчере задач или Process Explorer), а количество объектов GDI, обработчиков которых обрабатывается процессом, не уменьшается (программа запускается с ~ 30, открывая браузер он до ~ 140 и после закрытия браузера ~ 140 все еще открыт). Открытие другого браузера вызывает больше ручек и выделяет больше ресурсов. Кроме того, эта проблема не устраняется путем специального вызова Dispose() в элементе управления WebBrowser. Код прост и выглядит следующим образом:

Главное окно:

<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?

[UPDATE: обновленное сообщение для указания вызова Dispose() в соответствии с ответом itowlson - даже вызов Dispose() в элементе управления веб-браузером не освобождает ресурсы]

Ответ 1

В отличие от большинства элементов управления WPF, WebBrowser (поскольку он наследует от HwndHost) является IDisposable и инкапсулирует неуправляемые ресурсы. Окно WPF, в отличие от формы WinForms, автоматически не удаляет своих дочерних элементов (поскольку собственные элементы управления WPF не инкапсулируют неуправляемые ресурсы и не требуют удаления).

Добавьте к окну OnClosed переопределение (или обработайте закрытое событие) и вызовите Dispose в элементе управления WebBrowser.

Ответ 2

Вместо этого мы использовали элемент управления WinForm WebBrowser, который был создан внутри FormsHost в WPF, однако оба они работают одинаково с точки зрения пользовательского интерфейса, но мы обнаружили, что WebBrowser из WinForms имеет лучшую функциональность и лучшую производительность по сравнению с данными, приведенными в WPF.

Вы можете вручную удалить элемент управления WebBrowser из WinForm, который, безусловно, будет распоряжаться всеми его дочерними и бесплатными ресурсами, однако с моим прошлым опытом WinForm WebBrowser не выпускает 100% своих ресурсов после закрытия, но да, это намного лучше, чем WPF.

Ответ 3

Я не смог полностью решить проблему утечки, однако я заметил, что перемещение браузера в "около: пусто" до удаления определенно помогает уменьшить объем памяти, который висит вокруг.