Я работаю над веб-скребком, и он работает довольно хорошо. Он пройдет через тысячи страниц на большинстве сайтов и успешно завершится без проблем.
На нескольких сайтах я неоднократно вижу одну и ту же проблему.
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...)