Исключение нехватки памяти — неуправляемая память

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

На нескольких сайтах я неоднократно вижу одну и ту же проблему.

Insufficient memory to continue the execution of the program.

Изменить: я использовал perfmon, чтобы определить, что утечка происходит в неуправляемой памяти. Я знаю, потому что «частные байты» продолжают увеличиваться по мере работы программы, в то время как байты во всех кучах остаются стабильными.

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


Редактировать 2:

Я следовал указаниям на этом сайте: http://www.codeproject.com/Articles/42721/Best-Practices-No-5-Detecting-NET-application-memo

и я использовал debugDiag для проверки программы.

Проанализировав данные, отладочная диагностика сообщила мне, что послужило причиной утечки:

jscript.dll is responsible for 1.10 GBytes worth of outstanding allocations. The following are the top 2 memory consuming functions:



jscript!Parser::GenerateCode+167: 498.19 MBytes worth of outstanding allocations.

jscript!NoRelAlloc::PvAlloc+96: 292.99 MBytes worth of outstanding allocations.

Я не ссылаюсь на jscript.dll в своем приложении, он должен использоваться элементами управления веб-браузером, которые я использую.

System.Windows.Forms.WebBrowser

Это мое предположение, по крайней мере.

Я также получаю всплывающее окно сообщения с заголовком «Сообщение с веб-страницы», в котором говорится что-то вроде «недостаточно памяти в строке X».

Итак, я решил, что могу просто избавиться от объектов веб-браузера и вернуть свою память, поэтому я добавил кнопку со следующим кодом:

Me.wbMain.Dispose() 'dispose all of thwe web-browsers
frmDebugger.wbDebugMain.Dispose()
Me.WBNewWin.Dispose()

GC.Collect() 'just for the heck of it

Итак, поработав какое-то время, я перестал парсить и нажал новую кнопку... это вообще не имело никакого значения. Я смотрел общее количество «Частных байтов» в perfmon, и оно даже не двигалось.

Любые идеи, кто-нибудь?


Редактировать 3:

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

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

Я также слышал, что у IE7 была проблема, и что обновление до IE8 решит ее. У меня IE8, и он все еще теряет память.

Кто-то предположил, что минимизация формы с помощью элемента управления веб-браузером освободит часть памяти. Я пробовал, и это не имеет значения.

Мне также сказали, что я не должен ожидать, что использование памяти просто упадет, так как мне придется ждать сборщика мусора. Это не утечка в управляемом коде, поэтому GC.Collect() ничего не сделает. Он находится в неуправляемой памяти. По-видимому, функциональные возможности javascript используют другую память, и нет ручного способа принудительно собрать коллекцию. Но дело доходит до того, что он падает, так что, очевидно, есть проблема.

Я добавляю к этому вопросу вознаграждение в размере 50, и я награжу его всем, кто поможет мне решить утечку. Я хотел попробовать это решение: http://www.codeproject.com/Questions/322884/WPF-WebBrowser-control-vs-Internet-Explorer-browse, но я не могу понять, каким будет эквивалент vb.net. Я пробовал онлайн-конвертеры, и они ошибались при преобразовании этого кода (хотя они отлично работают для другого кода, который я преобразовал в прошлом)

Если мне не удастся устранить утечку, я присужу ее любому, кто преобразует упомянутую выше страницу с C# на vb.net.

Мой резервный план состоит в том, чтобы создать отдельное приложение, содержащее только веб-браузер, и взаимодействовать с этим процессом до тех пор, пока у него не закончится память, после чего я перезапущу его (память освобождается, когда я полностью закрываю свое приложение). Это далеко не идеально для моего приложения, так как веб-браузер довольно плотно вплетен в мой проект.


Изменить 4

Я попытался реализовать предложенную инъекцию javascript - вот мой код:

(Я запускаю его непосредственно перед переходом на новую страницу)

Public Shared Sub Clean_JS(ByRef wb As System.Windows.Forms.WebBrowser)

        Dim args As Object() = {"document.body"}

        Dim head As HtmlElement = wb.Document.GetElementsByTagName("head")(0)

        Dim scriptEl0 As HtmlElement = wb.Document.CreateElement("script")
        Dim element0 As mshtml.IHTMLScriptElement = DirectCast(scriptEl0.DomElement, mshtml.IHTMLScriptElement)
        element0.text = "function ReleaseHandler() {" + vbCrLf + "        var EvtMgr = (function() {" + vbCrLf + "            var listenerMap = {};" + vbCrLf + " " + vbCrLf + "            // Public interface" + vbCrLf + "            return {" + vbCrLf + "                addListener: function(evtName, node, handler) {" + vbCrLf + "                    node[""on"" + evtName] = handler;" + vbCrLf + "                    var eventList = listenerMap[evtName];" + vbCrLf + "                    if (!eventList) {" + vbCrLf + "                        eventList = listenerMap[evtName] = [];" + vbCrLf + "                    }" + vbCrLf + "                    eventList.push(node);" + vbCrLf + "                }," + vbCrLf + " " + vbCrLf + "                removeAllListeners: function() {" + vbCrLf + "                    for (var evtName in listenerMap) {" + vbCrLf + "                        var nodeList = listenerMap[evtName];" + vbCrLf + "                        for (var i = 0, node; node = nodeList[i]; i++) {" + vbCrLf + "                            node[""on"" + evtName] = null;" + vbCrLf + "                        }" + vbCrLf + "                    }" + vbCrLf + "                }" + vbCrLf + "            }" + vbCrLf + "        })();" + vbCrLf + "    }"
        head.AppendChild(scriptEl0)

        Dim scriptEl1 As HtmlElement = wb.Document.CreateElement("script")
        Dim element1 As mshtml.IHTMLScriptElement = DirectCast(scriptEl1.DomElement, mshtml.IHTMLScriptElement)
        element1.text = "function ReleaseHandler() {" + vbCrLf + "        var EvtMgr = (function() {" + vbCrLf + "            var listenerMap = {};" + vbCrLf + " " + vbCrLf + "            // Public interface" + vbCrLf + "            return {" + vbCrLf + "                addListener: function(evtName, node, handler) {" + vbCrLf + "                    node[""on"" + evtName] = handler;" + vbCrLf + "                    var eventList = listenerMap[evtName];" + vbCrLf + "                    if (!eventList) {" + vbCrLf + "                        eventList = listenerMap[evtName] = [];" + vbCrLf + "                    }" + vbCrLf + "                    eventList.push(node);" + vbCrLf + "                }," + vbCrLf + " " + vbCrLf + "                removeAllListeners: function() {" + vbCrLf + "                    for (var evtName in listenerMap) {" + vbCrLf + "                        var nodeList = listenerMap[evtName];" + vbCrLf + "                        for (var i = 0, node; node = nodeList[i]; i++) {" + vbCrLf + "                            node[""on"" + evtName] = null;" + vbCrLf + "                        }" + vbCrLf + "                    }" + vbCrLf + "                }" + vbCrLf + "            }" + vbCrLf + "        })();" + vbCrLf + "    }"
        head.AppendChild(scriptEl1)

        wb.Document.InvokeScript("ReleaseHandler")
        wb.Document.InvokeScript("purge", args)


End Sub

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

может ли кто-нибудь увидеть какие-либо недостатки в моей логике? Я пытаюсь реализовать это исправление: http://www.codeproject.com/Questions/322884/WPF-WebBrowser-control-vs-Internet-Explorer-browse

кстати - я протестировал его, используя простой код, такой как этот:

object[] args = {"my important message"};
webBrowser1.Document.InvokeScript("alert",args);

и это:

Dim head As HtmlElement = wb.Document.GetElementsByTagName("head")(0)
Dim scriptEl As HtmlElement = wb.Document.CreateElement("script")
Dim element As mshtml.IHTMLScriptElement = DirectCast(scriptEl.DomElement, mshtml.IHTMLScriptElement)
element.text = "function sayHello() { alert('hello') }"
head.AppendChild(scriptEl)
wb.Document.InvokeScript("sayHello")

и он показал сообщение в обоих тестовых случаях.

Любопытно, когда я попытался протестировать внедрение скрипта, выполнив следующие действия:

    Dim head As HtmlElement = wbMain.Document.GetElementsByTagName("head")(0)
    Dim scriptEl As HtmlElement = wbMain.Document.CreateElement("script")
    Dim element As mshtml.IHTMLScriptElement = DirectCast(scriptEl.DomElement, mshtml.IHTMLScriptElement)
    element.text = "function sayHello() { alert('hello') }"
    head.AppendChild(scriptEl)
    wbMain.Document.InvokeScript("sayHello")


    RTB_RawHTML.Text = "TEST" + vbCrLf + wbMain.DocumentText

Я не видел введенный код, отраженный в текстовом поле - единственное изменение, которое я увидел, это появление слова «тест» (я запускаю код RTB_RawHTML.Text = wbMain.DocumentText, когда страницы заканчивают загрузку из события documentCompleted...)


person Allen    schedule 15.02.2013    source источник
comment
Пробовали ли вы его отладить (нажмите F5) и посмотреть, какие размеры задействованных объектов находятся на сайте, вызывающем проблему? Возможно, разумное использование StringBuilders вместо Strings для переменных, которые часто используются повторно (кажется маловероятным, если это работает для большинства сайтов).   -  person Andrew Morton    schedule 16.02.2013
comment
Кажется, у меня где-то неуправляемая утечка памяти. Я думаю, что раздел, на который я первоначально смотрел, может быть жертвой, а не причиной проблемы... обычно он падает там, потому что на самом деле использует приличный объем памяти... однако я недавно заметил, что программа постепенно сливала неуправляемую память - но я не знаю откуда. Я обновил свой пост выше.   -  person Allen    schedule 22.02.2013
comment
Вместо использования MSHTML вы могли бы получить HTML-код с помощью WebClient и проанализировать его с помощью чего-то вроде Html Agility Pack ( htmlagilitypack.codeplex. ком )?   -  person Andrew Morton    schedule 24.02.2013
comment
Я использовал еще несколько инструментов отладки, чтобы исследовать проблему... после просмотра того, что они мне говорили, я не думаю, что MSHTML несет ответственность за проблему... я думаю, что это может быть элемент управления веб-браузером... я обновил свой вопрос выше   -  person Allen    schedule 28.02.2013
comment
Есть много статей, доступных, если вы ищете утечку памяти vb.net управления веб-браузером. Но если вам нужен только необработанный HTML, а не то, что может быть сгенерировано Javascript на странице, то использование WebClient для загрузки необработанного HTML будет проще и быстрее.   -  person Andrew Morton    schedule 28.02.2013
comment
многие из сайтов, с которыми я имею дело, используют javascript или другие технологии, а не просто необработанный html - я использовал элемент управления веб-браузером, чтобы я мог обрабатывать и эти случаи. Я ищу в сети утечку памяти vb.net управления веб-браузером, но еще не нашел решения. Я обнаружил, что это распространенная проблема.   -  person Allen    schedule 01.03.2013
comment
Похоже, у этого есть решение: codeproject.com /Вопросы/322884/   -  person Andrew Morton    schedule 01.03.2013
comment
я проверил это, но это было в С#... к сожалению, все онлайн-конвертеры либо разбились, либо сгенерировали неверный код при попытке преобразовать его в vb... я, вероятно, в конечном итоге попытаюсь преобразовать его.. , единственный другой вариант, который я могу придумать, - это создать новый процесс с помощью wb, а затем некоторое время общаться с ним, пока его использование памяти не увеличится, а затем переустановить его ... что, вероятно, было бы трудно реализовать в моем случае   -  person Allen    schedule 02.03.2013
comment
@Allen - код в упомянутой статье - это javascript, а не C #.   -  person JDB still remembers Monica    schedule 06.03.2013


Ответы (2)


Код в вашей упомянутой статье не C #, это Javascript. Я полагаю, что идея заключалась бы в том, чтобы внедрить JS в вашу HTML-страницу, чтобы он мог запускаться при выгрузке страницы, что очистит существующие события JS.

Вы можете прочитать эту статью о добавлении JS на страницу в элементе управления WebBrowser:
http://www.codeproject.com/Articles/94777/Adding-a-Javascript-Block-Into-a-Form-Hosted-by-We

Dim scriptText As String =
    <string>
        function ReleaseHandler() {
                var EvtMgr = (function() {
                    var listenerMap = {};

                    // Public interface
                    return {
                        addListener: function(evtName, node, handler) {
                            node["on" + evtName] = handler;
                            var eventList = listenerMap[evtName];
                            if (!eventList) {
                                eventList = listenerMap[evtName] = [];
                            }
                            eventList.push(node);
                        },

                        removeAllListeners: function() {
                            for (var evtName in listenerMap) {
                                var nodeList = listenerMap[evtName];
                                for (var i = 0, node; node = nodeList[i]; i++) {
                                    node["on" + evtName] = null;
                                }
                            }
                        }
                    }
                })();
            }

        function purge(d){
            var a = d.attributes, i, l, n;
            if (a) {
                for (i = a.length - 1; i >= 0 ; i -= 1) {
                    n = a[i].name;
                    if (typeof d[n] === 'function') {
                        d[n] = null;
                    }
                }
            }
            a = d.childNodes;
            if (a) {
                l = a.length;
                for (i = 0; i < l; i += 1) {
                    purge(d.childNodes[i]);
                }
            }
        }

    <string>

Dim head As HtmlElement = webBrowser1.Document.GetElementsByTagName("head")(0)
Dim script As HtmlElement = webBrowser1.Document.CreateElement("script")
Dim domElement As IHTMLScriptElement = CType(script.DomElement, IHTMLScriptElement)
domElement.text = scriptText
head.AppendChild(script)

Я не тестировал этот код (я не совсем уверен, как бы я это сделал, поскольку вы сами не предложили пример кода)... это скорее предложение о том, как вы могли бы действовать. Я никогда не пытался вставить JS в элемент управления WebBrowser, поэтому я не совсем уверен, как вы собираетесь его выполнять (поскольку теоретически JS уже будет выполнен после загрузки страницы, поэтому ваш внедренный JS будет «опоздать на вечеринку»).

Вам также потребуется найти способ подключить документ таким образом, чтобы он вызывал обе эти функции при выгрузке. Идея состоит в том, чтобы устранить утечки памяти JS, исключив объекты и события JS, поэтому простого объявления функций недостаточно. Я видел много статей в Интернете, в которых обсуждается, как событие OnBeforeUnload нарушается в элементе управления WebBrowser (оно не срабатывает правильно), так что у вас может быть довольно много работы.

person JDB still remembers Monica    schedule 06.03.2013

Может быть, вы можете попробовать код, чтобы не сохранять файл cookie на компьютере пользователя. Потому что временный элемент может вызвать несколько проблем с компьютером пользователя.

person Kasnady    schedule 07.03.2013
comment
Тот факт, что файлы cookie обрабатываются элементом управления веб-браузером, является одной из причин, по которым я его использую... я не хочу писать это сам. Кроме того, я знаю, что это должно быть связано с javascript, поскольку debugdiag говорит мне следующее: jscript.dll is responsible for 1.10 GBytes worth of outstanding allocations. - person Allen; 08.03.2013