Out Of Memory Exception - unmanaged memory

I am working on a web scraper, and it gernally works quite well. It will go through thousands of pages on most sites and complete sucessfully with no issues.

On a few sites, I am repeatedly seeing the same issue.

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 для проверки программы.

После анализа данных debug diag сказал мне, что было причиной утечки:

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

Итак, после запуска на некоторое время, я перестал чистить и нажал мою новую кнопку... это не имело никакого значения вообще. Я смотрел общее количество "Private Bytes" в perfmon, и оно даже не двигалось.

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


Изменить 3:

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

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

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

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

Мне также сказали, что я не должен ожидать, что использование памяти просто упадет, так как мне придется ждать сборщика мусора. Это не утечка в управляемом коде, поэтому 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

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

Кто-нибудь может увидеть какие-либо недостатки в моей логике? Я пытаюсь реализовать это исправление: 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...)

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 (он не срабатывает правильно), так что вам может потребоваться довольно много работы.

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