34

同期呼び出しはUIレンダリングをブロックするため、同期ajax呼び出しの使用に対して与えられるこの一般的なアドバイスを理解しています。

一般的に与えられる他の理由は、同期AJAXでのメモリリークの問題です。

MDNドキュメントから-

注:ネットワークは本質的に非同期であるため、同期リクエストを使用するとメモリやイベントがリークする可能性があるため、同期XMLHttpRequestを使用しないでください。唯一の例外は、同期リクエストがワーカー内で適切に機能することです。

同期呼び出しはどのようにメモリリークを引き起こす可能性がありますか?

実用的な例を探しています。このトピックに関する文献へのポインタは素晴らしいでしょう。

4

5 に答える 5

3

XHR ブロック スレッドの実行と、このスレッドの関数実行スタック内のすべてのオブジェクトを GC から同期します。

例えば:

function (b) { 
  var a = <big data>;
  <work with> a and b
  sync XHR
}

変数 a と b はここでブロックされます (そしてスタック全体も)。そのため、GC が機能し始めた後に同期 XHR がスタックをブロックした場合、すべてのスタック変数は「生き残った GC」としてマークされ、初期のヒープからより永続的なヒープに移動されます。そして、単一の GC でさえ生き残らないはずのオブジェクトのトーンは、多くのガベージ コレクションに存在し、これらのオブジェクトからの参照でさえも GC で生き残ります。

クレーム スタック ブロック GC と、長寿命オブジェクトとしてマークされたそのオブジェクトについて: セクション Conservative Garbage Collection in Clawing Our Way Back To Precision を参照してください。また、通常のヒープが GCされたに「マークされた」オブジェクトが GC されます。通常は、まだメモリを解放する必要がある場合にのみ行われます (マークされてスイープされたオブジェクトの収集には時間がかかるため)。

更新:初期ヒープの効果のないソリューションだけでなく、本当にリークですか? 考慮すべき点がいくつかあります。

  • リクエストが終了した後、これらのオブジェクトはどのくらいロックされますか?
  • Sync XHR はスタックを無制限にブロックできます。XHR には (IE 以外のすべてのブラウザーで) タイムアウト プロパティがありません。ネットワークの問題はまれではありません。
  • ロックされている UI 要素の量は? わずか 1 秒間で 20M のメモリをブロックした場合 == 2 分で 200k リード。多くの背景タブを検討してください。
  • 単一の同期がリソースのトーンをブロックし、ブラウザがファイルをスワップするケースを検討してください
  • 別のイベントが DOM を変更しようとすると、同期 XHR によってブロックされる可能性があり、別のスレッドがブロックされます (また、スタック全体もブロックされます)。
  • ユーザーが XHR の同期につながるアクションを繰り返すと、ブラウザ ウィンドウ全体がロックされます。ブラウザーは、最大 = 2 スレッドを使用してウィンドウ イベントを処理します。
  • ブロックしなくても、スレッド、クリティカル セクション リソース、UI リソース、DOM など、OS とブラウザの内部リソースを大量に消費します。(メモリの問題により) 同期 XHR を使用するサイトで 10 個のタブ、およびサイトで 100 個のタブを開くことができると想像してください。非同期 XHR を使用します。これはメモリリークではありませんか。
于 2013-08-22T07:00:45.877 に答える
3

ガベージ コレクタが機能しないことが主な原因で、メモリ リークが発生していると思います。つまり、何かへの参照があり、GC はそれを削除できません。簡単な例を書きました:

var getDataSync = function(url) {
    console.log("getDataSync");
    var request = new XMLHttpRequest();
    request.open('GET', url, false);  // `false` makes the request synchronous
    try {
        request.send(null);
        if(request.status === 200) {
            return request.responseText;
        } else {
            return "";
        }
    } catch(e) {
        console.log("!ERROR");
    }
}

var getDataAsync = function(url, callback) {
    console.log("getDataAsync");
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url, true);
    xhr.onload = function (e) {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                callback(xhr.responseText);
            } else {
                callback("");
            }
        }
    };
    xhr.onerror = function (e) {
        callback("");
    };
    xhr.send(null);
}

var requestsMade = 0
var requests = 1;
var url = "http://missing-url";
for(var i=0; i<requests; i++, requestsMade++) {
    getDataSync(url);
    // getDataAsync(url);
}

同期関数が多くのものをブロックするという事実を除いて、別の大きな違いがあります。エラー処理。getDataSyncを使用して try-catch ブロックを削除し、ページを更新すると、エラーがスローされることがわかります。これは URL が存在しないためですが、問題は、エラーがスローされたときにガベージ コレクターがどのように機能するかです。エラーに関連するすべてのオブジェクトをクリアしますか、エラーオブジェクトを保持しますか、そのようなものですか。どなたかご存知の方がいらっしゃいましたら、ここに書き込んでいただけると幸いです。

于 2013-08-17T12:31:18.333 に答える
0

同期 AJAX リクエストを使用したメモリ リークは、多くの場合、次の原因で発生します。

  • setInterval/setTimout を使用すると循環呼び出しが発生します。
  • XmlHttpRequest - 参照が削除されると、xhr にアクセスできなくなります

メモリ リークは、ブラウザが何らかの理由で不要になったオブジェクトからメモリを解放しない場合に発生します。

これは、ブラウザーのバグ、ブラウザーの拡張機能の問題、そしてごくまれに、コード アーキテクチャの間違いが原因で発生する可能性があります。

新しいコンテキストで setInterval を実行したときに発生するメモリ リークの例を次に示します。

var
Context  = process.binding('evals').Context,
Script   = process.binding('evals').Script,
total    = 5000,
result   = null;

process.nextTick(function memory() {
  var mem = process.memoryUsage();
  console.log('rss:', Math.round(((mem.rss/1024)/1024)) + "MB");
  setTimeout(memory, 100);
});

console.log("STARTING");
process.nextTick(function run() {
  var context = new Context();

  context.setInterval = setInterval;

  Script.runInContext('setInterval(function() {}, 0);',
                      context, 'test.js');
  total--;
  if (total) {
    process.nextTick(run);
  } else {
    console.log("COMPLETE");
  }
});
于 2013-08-16T16:34:30.637 に答える