2

私はウェブスクレイパーに取り組んでいます、そしてそれは一般的に非常にうまく機能します。ほとんどのサイトで数千ページを通過し、問題なく正常に完了します。

いくつかのサイトで、同じ問題が繰り返し発生しています。

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を使ってプログラムを調べました。

データを分析した後、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を参照していません。使用しているWebブラウザコントロールで使用されている必要があります。

System.Windows.Forms.WebBrowser

少なくとも、それは私の推測です。

また、「X行のメモリ不足」の影響を示す「Webページからのメッセージ」というタイトルのメッセージボックスが表示されます。

だから、私はウェブブラウザオブジェクトを破棄してメモリを取り戻すことができると考えました-それで私は次のコードでボタンを追加しました:

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

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

それで、しばらくそれを実行した後、私はこするのをやめて、私の新しいボタンをクリックしました...それはまったく違いがありませんでした。perfmonで「PrivateBytes」の合計を見ていましたが、動かなかったのです。

何かアイデアはありますか?


編集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に変換した人に賞を授与します。

私のフォールバックプランは、Webブラウザーのみを含む別のアプリケーションを作成し、メモリが不足するまでそのプロセスと通信し、その時点で再起動します(アプリケーションを完全に終了すると、メモリが解放されます)。Webブラウザーは私のプロジェクトにかなりしっかりと組み込まれているため、これは私のアプリケーションにとって理想的とはほど遠いものです。


編集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

挿入されたコードがテキストボックスに反映されていませんでした。表示された唯一の変更は、「test」という単語が表示されたことです(documentCompletedイベントからのページの読み込みが終了したときにコードRTB_RawHTML.Text = wbMain.DocumentTextを実行します...)

4

2 に答える 2

0

Cookie をユーザーのコンピューターに保存しないようにコードを試すことができるかもしれません。一時的なアイテムが原因で、ユーザーのコンピューターにいくつかの問題が発生する可能性があります

于 2013-03-07T02:26:23.630 に答える
0

参照した記事のコードは C# ではなく、Javascript です。JS を HTML ページに挿入して、ページのアンロード時に実行できるようにすることで、既存の JS イベントを一掃することが考えられると思います。

WebBrowser コントロールのページに JS を追加する方法については、次の記事を参照してください

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 コントロールでどのように壊れているか (正しく起動しない) について議論している記事をオンラインでたくさん見たので、かなりの作業が必要になるかもしれません。

于 2013-03-06T14:11:48.830 に答える