Как исправить утечку памяти в элементе управления IE WebBrowser? - PullRequest
29 голосов
/ 24 мая 2009

Я пытаюсь встроить элемент управления WebBrowser в приложение C # Winform. Это звучит достаточно просто. Однако я обнаружил, что элемент управления WebBrowser поглощает много памяти каждый раз, когда я вызываю метод Navigate. Память никогда не освобождается. Использование памяти растет и растет…

У многих людей в сети точно такая же проблема, но я пока не нашел удовлетворительного ответа. Это лучшее обсуждение этой проблемы, которое я нашел до сих пор:

Утечка памяти в IE WebBrowser Control

Один человек предложил обновить до IE8, чтобы исправить проблему.

Однако мне нужно решение, которое работает независимо от того, установлена ​​ли у пользователя последняя версия IE. У меня нет контроля над пользовательской средой.

Кто-нибудь знает, как освободить память, занятую элементом управления WebBrowser? Есть ли обходные пути? Существуют ли альтернативы элементу управления WebBrowser?

Обновление: Я только что сделал еще несколько тестов. На работе я использую Windows XP и IE6. Память там не растет. Память увеличивается при вызове метода навигации, но освобождается через некоторое время. Дома я использую Vista и обновился до IE8. Здесь я тоже не вижу проблемы больше. Похоже, что проблема специфична для IE7. Поэтому вопрос следует перефразировать так: «Как исправить утечку памяти в IE WebBrowser Control, когда установлен IE7». Кто-нибудь может подтвердить, что эта проблема специфична для IE7?

Ответы [ 18 ]

11 голосов
/ 07 июня 2011

мое приложение также постоянно потребляло память при навигации и больше не выпускало. я нашел решение для меня здесь: http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/88c21427-e765-46e8-833d-6021ef79e0c8

для полноты изложения пост заметный отрывок:

-- in class definition

    [DllImport("KERNEL32.DLL", EntryPoint = "SetProcessWorkingSetSize", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
    internal static extern bool SetProcessWorkingSetSize(IntPtr pProcess, int dwMinimumWorkingSetSize, int dwMaximumWorkingSetSize);

    [DllImport("KERNEL32.DLL", EntryPoint = "GetCurrentProcess", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
    internal static extern IntPtr GetCurrentProcess();

- код для вызова, когда вы хотите уменьшить память

        IntPtr pHandle = GetCurrentProcess();
        SetProcessWorkingSetSize(pHandle, -1, -1);

все почести: http://social.msdn.microsoft.com/profile/mike_t2e/?type=forum&referrer=http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/88c21427-e765-46e8-833d-6021ef79e0c8 для размещения решения.

и http://ict -engineer.blogspot.com / 2010/10 / нетто-WebBrowser-контроль-памяти leak.html для SEO'а это правильно, чтобы я мог найти его;)

привет

edit: если это поможет вам быстро решить проблему - хорошо. но вы должны переоценить дизайн своего приложения, шаблон, который вы используете, если таковой имеется, рефакторировать объект, если вы будете использовать его гораздо дольше ...

8 голосов
/ 25 мая 2009

Я только что создал простое приложение с контролем веб-браузера, чтобы попытаться повторить ваши результаты. Я обнаружил, что да, каждый раз, когда вы переходите на страницу, используемая память значительно увеличивается. ОДНАКО, это НЕ утечка памяти, потому что если вы продолжите навигацию, вы увидите, что через некоторое время память значительно падает, указывая на то, что сборщик мусора сделал свое дело. Чтобы доказать это, я заставлял сборщик мусора собирать данные после каждого вызова Navigate, а общий объем используемой памяти оставался на прежнем уровне после каждого вызова навигации.

Так что, несмотря на то, что он НАБИРАЕТ память каждый раз, когда вы «перемещаетесь», это НЕ утечка памяти, и вы освободите память. Если он набирает слишком быстро, просто вызовите GC.Collect ();

5 голосов
/ 12 июня 2015

ОСНОВНАЯ ИДЕЯ есть,

«Убей себя и возродись».

Windows решит все проблемы с памятью.

но если вы сначала закроете приложение, вы не сможете запустить новое.

Итак, НАЧНИТЕ НОВУЮ, ЗАКРЫТЬ СТАРУЮ Сначала включите новый и выключите старый.


public void SOLVE_ALL_MY_MEMORY_PROBLEM()
{
  System.Diagnostics.Process.Start("MyProgram.exe");
  Application.Exit();
}

https://www.youtube.com/watch?v=aTBlKRzNf74

Если есть параметр,

public void SOLVE_ALL_MY_MEMORY_PROBLEM()
{
  System.Diagnostics.Process.Start("MyProgram.exe", "PARA_para_dance");
  Application.Exit();
}

Перейти к Program.cs

    static void Main(string[] args)
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        if(args.Count() > 0)
            Application.Run(new Form1(args[0]));
        else
            Application.Run(new Form1());
    }

и, перейдите к Form1.cs и создайте еще одну Form1 ()

    public Form1()
    {
        InitializeComponent();
    }

    public Form1(string dance_name)
    {
        InitializeComponent();

        ...
    }

или вы можете использовать временный файл !!!

3 голосов
/ 09 марта 2017

Согласно MSDN , элемент управления System.Windows.Forms.WebBrowser является управляемой оболочкой для элемента управления ActiveX WebBrowser и использует любую версию элемента управления, установленную на компьютере пользователя.

Вы можете найти метод Dispose (bool) в метаданных класса WebBrowser (нажмите F12 в Visual Stuio) для освобождения неуправляемого ресурса. (NOT Dispose ())

код здесь

protected override void Dispose(bool disposing) {
    if (disposing) {
        if (htmlShimManager != null)
        {
            htmlShimManager.Dispose();
        }
        DetachSink();
        ActiveXSite.Dispose();
    }
    base.Dispose(disposing);
}

Но если вы попытаетесь вызвать WebBrowser.Dispose (bool), отобразится ошибка компилятора CS1540 .

Класс WebBrowser поддерживает метод Dispose (bool), НО мы не можем его использовать.
Я думаю, что класс WebBrowser был спроектирован неправильно.

У меня есть идея вызвать WebBrowser.Dispose (true).
ОЧЕНЬ ПРОСТО! но это не очень хороший способ.

Пример кода здесь (нужно 3 кнопки и 1 TextBox)

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Test_20170308_01
{
    public partial class Form1 : Form
    {
        [DllImportAttribute("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)]
        private static extern int SetProcessWorkingSetSize(IntPtr process, int minimumWorkingSetSize, int maximumWorkingSetSize);
        public static void FlushMemory()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
            {
                SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
            }
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void addWeb()
        {
            WebBrowserD webBrowser1 = new WebBrowserD();
            webBrowser1.Size = new Size(1070, 585);
            this.Controls.Add(webBrowser1);
            webBrowser1.Navigate("about:blank");
        }

        private void RemoveWeb()
        {
            foreach (Control ctrl in this.Controls)
            {
                if (ctrl is  WebBrowserD)
                {
                    WebBrowserD web = (WebBrowserD)ctrl;
                    this.Controls.Remove(ctrl);
                    web.Navigate("about:blank");
                    web.Dispose(true);
                    FlushMemory();
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            addWeb();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            RemoveWeb();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            foreach (Control ctrl in this.Controls)
            {
                if (ctrl is WebBrowserD)
                {
                    WebBrowserD axweb = (WebBrowserD)ctrl;
                    axweb.Navigate(textBox1.Text);
                    FlushMemory();
                }
            }
        }
    }

    public class WebBrowserD : WebBrowser
    {
        internal void Dispose(bool disposing)
        {
            // call WebBrower.Dispose(bool)
            base.Dispose(disposing);
        }
    }
}

Этот код может предотвратить утечку памяти.

В итоге Вам нужен только один класс.

    public class WebBrowserD : WebBrowser
    {
        internal void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }
    }
3 голосов
/ 24 мая 2009

Существует альтернативный элемент управления , который использует Gecko (движок Firefox использует) вместо Trident и очень хорошо работает с интерфейсами MSHTML.

Ваши страницы будут отображаться в Gecko, и вы будете иметь полный контроль над настройками, плагинами, безопасностью и любыми другими настраиваемыми функциями браузера.

Недостатком является то, что вам нужно будет поставлять Gecko вместе с вашим приложением, в последний раз я использовал эквивалент Firefox 2, и он занимал около 8 МБ.

Я недавно выпустил приложение, в котором сравнивали рендеринг IE и Firefox друг с другом, и оба обновлялись по мере того, как вы редактировали CSS. У меня не было проблем с памятью, которые у вас были с веб-браузером, но я нашел, что с Gecko очень легко работать. Он не имеет того же класса управляемых оболочек, что и элемент управления .net WebBrowser, но его достаточно легко обойти.

1 голос
/ 22 марта 2014

Кажется, что метод Navigate () сохраняет все посещенные страницы в памяти, так как вы можете использовать метод GoBack (), на самом деле "утечки памяти" нет. Моя программа посещает один и тот же URL несколько раз. Проблема «утечки памяти» может быть устранена с помощью метода Refresh () instand метода Navigate (), за которым следует GC.Collect (). Код в следующем:

       try
        {
            if (webBrowser.Url.Equals("about:blank")) //first visit
            {
                webBrowser.Navigate(new Uri("http://url"));
            }
            else
            {
                webBrowser.Refresh(WebBrowserRefreshOption.Completely);
            }
        }
        catch (System.UriFormatException)
        {
            return;
        }
        System.GC.Collect(); // may be omitted, Windows can do this automatically
1 голос
/ 03 января 2011

В элементе управления WebBrowser есть известная утечка памяти. См. Следующую статью Microsoft KB - KB893629 .

1 голос
/ 14 марта 2014

Я столкнулся с этой проблемой при написании небольшого приложения для создания слайд-шоу для разных страниц интрасети, используемых моей компанией. Самым простым решением, которое я нашел, было перезапуск приложения через некоторое фиксированное время, в моем случае - час. Это решение хорошо сработало для нас, поскольку пользователь не очень активно взаимодействовал с браузером.

Public Class MyApplication

    Private _AppTimer As Timers.Timer

    Public Sub New()
        _AppTimer = New Timers.Timer()
        _AppTimer.Interval = 1 * 60 * 60 * 1000 '1 Hour * 60 Min * 60 Sec * 1000 Milli

        AddHandler _AppTimer.Elapsed, AddressOf AppTimer_Elapsed

        _AppTimer.Start()
    End Sub

    Private Sub AppTimer_Elapsed(s As Object, e As Timers.ElapsedEventArgs)
        Application.Restart()
    End Sub

End Class

Это предполагает, конечно, что у вас есть механизм сохранения данных.

0 голосов
/ 25 июля 2017

Это почти конец 2017 года, и все же эта досадная ошибка присутствует в WebBrowser.

Я перепробовал все решения здесь, и ни одно из них не помогло мне. Утечка памяти все еще сохраняется ... Самое странное, что когда я звоню:

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

IntPtr pHandle = GetCurrentProcess();
SetProcessWorkingSetSize(pHandle, -1, -1);

Это действительно уменьшает память! но когда вызывается следующая инструкция Navigate, вся просочившаяся память возвращается в область видимости .... например (если объем памяти составляет 450 МБ .. эта инструкция уменьшается примерно до 20 МБ и сразу после повторного вызова .Navigate (string) она переходит к 460 МБ и утечка памяти продолжается ...

Я даже пытался использовать Dispose (), переходя к пункту about: blank перед следующей страницей, устанавливая нулевой объект webbrowser и создавая новый. Все эти попытки приводят к утечке памяти ... это действительно расстраивает ... Какие-нибудь другие решения?

0 голосов
/ 15 мая 2016

Это сработало для меня, не уверен, что если 100% очищает память, кажется, что он все еще сохраняет кэшированные страницы, но больше не использует 500 МБ оперативной памяти, он остается на 60 МБ.

моя программа неоднократно переходит на один и тот же сайт, на 3 разные страницы, только один раз за использование, без сканирования большого количества страниц или чего-либо еще.

string eventBuffer;

void GetContracts_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            var web = sender as WebBrowser;
            if (web.Url == e.Url)
            {
                TaskMaster.Get_Contracts(ref web);
                if(Memory.Contracts.Count==0)
                {
                    eventBuffer="UpdateContractFailed";
                    web.Disposed += new EventHandler(web_Disposed);
                    web.Dispose();
                    return;
                }
                eventBuffer="UpdateContractList";
                web.Disposed += new EventHandler(web_Disposed);
                web.Dispose();
            }
        }

private void web_Disposed(object sender, EventArgs e)
        {
            FireEvent(eventBuffer);
            GC.Collect();
            thread.Abort();
        }
...