Проблема с памятью WebBrowser

У меня есть приложение .NET, для которого требуется использовать WebBrowser, чтобы автоматически перемещаться по нескольким страницам. Но если я зайду, например, в Google и настрою Google Instant on, а затем что-то пробую и перемещаюсь вручную через следующую кнопку несколько раз, память, используемая моим приложением, начнет увеличиваться.

Проблема может заключаться в том, что Google Instant каким-то образом сохраняет данные с предыдущих страниц, но даже после того, как я перемещаюсь в другом месте, например "около: пусто", используемая память не будет уменьшаться. Эта проблема также возникает с IE 9. Я начал записывать свою память, используемую на странице 60, и это то, что я получил (с IE 9):

Page 60: 180 MB
Page 70: 214 MB
Page 80: 245 MB
Page 90: 280 MB

Итак, как вы можете видеть, память увеличивается почти линейно на 30-35 МБ каждые 10 страниц. Это не будет проблемой, если память будет выпущена после перехода от Google. Но нет.

Я также пробовал этот и ничего не делал.

Изменить: Я сделал проект, чтобы проверить это. Вот мой код Form1:

namespace WebBrowserMemoryTest
{
    public partial class Form1 : Form
    {
        private int _Pages;

        public Form1()
        {
            InitializeComponent();
            webBrowser1.Navigate("http://www.google.com");
        }

        private void startButton_Click(object sender, EventArgs e)
        {
            _Pages = 0;
            timer1.Start();
        }

        private void stopButton_Click(object sender, EventArgs e)
        {
            timer1.Stop();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            HtmlElement next = webBrowser1.Document.GetElementById("pnnext");

            if (_Pages <= 90)
            {
                if (null != next)
                {
                    string href = next.GetAttribute("href");
                    webBrowser1.Navigate(href);
                    _Pages++;
                }
                else
                {
                    timer1.Stop();
                    MessageBox.Show("Next button not found");
                }
            }
            else
            {
                timer1.Stop();
                MessageBox.Show("Done");
            }
        }

        private void goButton_Click(object sender, EventArgs e)
        {
            webBrowser1.Navigate(textBox1.Text);
        }

        private void freeMemButton_Click(object sender, EventArgs e)
        {
            MemoryManagement.FlushMemory();
        }
    }

    public class MemoryManagement
    {
        [DllImport("kernel32.dll")]
        public static extern bool SetProcessWorkingSetSize(IntPtr proc, int min, int max);

        public static void FlushMemory()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
            {
                SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
            }
        }
    }
}

То, что я делаю, - это поиск в Google с Google с помощью Google Instant на, а затем нажмите startButton (который вызывает startButton_Click). Примерно через 80 страниц я нажимаю stopButton, затем перехожу к "about: blank", затем возвращаюсь в Google и просматриваю что-нибудь еще и снова нажимаю startButton.

Я сначала протестировал это на своем ПК, у которого 6 ГБ оперативной памяти. Когда я достиг 1,5 ГБ, приложение перестало отвечать, но я не получил исключение OutOfMemory. Затем я протестировал его на виртуальной машине с Windows 7 и 1 ГБ оперативной памяти. Когда он достиг около 300 МБ, веб-браузер в моем приложении стал невосприимчивым.

Если я нажимаю кнопку freeMem, которая вызывает freeMemButton_Click, память возвращается (но см. мой Edit2). Так что "решает" мою проблему. Но теперь мой вопрос, почему мне нужно позвонить SetProcessWorkingSetSize? Разве Windows не должна автоматически освобождать память? Кроме того, я не уверен, что вызов этой функции будет иметь какой-либо побочный эффект.

Я уверен, что это ошибка. Должен ли я идти вперед и сообщать об этом?

Edit2: Я протестировал решение Стефана (вызов SetProcessWorkingSetSize(GetCurrentProcess(), -1, -1)) и не исправил его. Память опустилась на диспетчер задач, но это видно только. Приложение перестало отвечать на запросы после того, как многие браузерные навигации потребовались, чтобы перестать отвечать на запросы, не вызывая эту функцию.

Ответ 1

Windows действительно не возвращает освобожденную память, если нет причин для этого. И единственная причина была бы в том, что для другого приложения требуется эта память, и нет никакой другой памяти. Поэтому кажется, что использование памяти увеличивается.

Попробуйте позвонить

SetProcessWorkingSetSize (GetCurrentProcess(), -1, -1);

иногда - это заставит ОС вернуть всю освобожденную память обратно в ОС.

Ответ 2

Я думаю, что htmlElement pnext не выпущен, так как это ComObject, и может быть ошибка в браузере.

Попробуйте pnext.Release или попробуйте Marshal.Release и получите экземпляр ComObject для выпуска.

Ответ 3

при работе с Webbrowser я обнаружил, что важно дождаться события "documentcompleted" и проверить, завершено ли "состояние" без перехода на следующую страницу. Else отмените навигацию и снова подождите, чтобы законченное состояние (прервано) не было выполнено.

может заключаться в том, что использование таймеров, а не проверка состояния Webbrowser может быть проблемой

следующая вещь с Webbrowser-Control заключается в том, что вы не можете использовать ее в многопоточном /Backgroundworker, поскольку она требует STA.... (часто создает проблемы с невосприимчивым приложением)

решение для "ful-contol" (разбор веб-сайтов) с использованием .NET для меня состояло в том, чтобы использовать WebRequest/Webresponse, вы можете снова привести результат этого к Webbroswer.Document, если вы хотите, чтобы DOM был "авто", выполнял, использовал его многопоточно, легко устанавливал таймауты/прокси, которые я нашел более удобными, чем использование управления Webbrowser.

Тем не менее я успешно реализовал страницы синтаксического анализа Webbrowser-Control в другом проекте, используя подсказки отсюда (Как исправить утечку памяти в IE WebBrowser Control?)

еще один "забавный" тон с Winforms-программированием, который я обнаружил, заключается в том, что кажется, что GC.Collect запускается, когда окно сведено к минимуму снова открытое → это уменьшает использование mem-use? (maybee Stefan post также ссылается на эту проблему)