Утечка памяти при использовании элемента управления 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?

[ОБНОВЛЕНИЕ: обновленное сообщение, чтобы указать вызов Dispose() согласно ответу itowlson - даже вызов Dispose() в элементе управления веб-браузера не освобождает ресурсы]

4 ответа

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

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

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

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

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

Я приехал сюда в поисках ответов на утечки моей памяти. Я знаю, что прошло 11 лет, в любом случае я хотел бы поделиться тем, что я только что узнал.

      <WebBrowser Grid.Row="0" x:Name="_browser" />

Использование x:Name в xaml само по себе является источником утечки памяти.

Эта утечка происходит из-за следующей особенности WPF: WPF создает сильную глобальную ссылку на элемент пользовательского интерфейса, объявленный в XAML, и использует директиву x:Name.

Другие вопросы по тегам