1. ホーム
  2. javascript

[解決済み] JavaScriptのクロージャはどのようにガベージコレクションされるのか

2022-04-23 04:05:37

質問

以下のようなログを記録しました。 Chromeのバグ これは、私のコードに多くの深刻で明白でないメモリリークを引き起こしました。

(この結果はChrome Dev Toolsの メモリプロファイラ GCを実行し、収集されたガーベッジされていないすべてのヒープスナップショットを取ります)。

以下のコードでは someClass インスタンスはガベージコレクションされます(良いことです)。

var someClass = function() {};

function f() {
  var some = new someClass();
  return function() {};
}

window.f_ = f();

しかし、この場合ガベージコレクションされない(悪い)。

var someClass = function() {};

function f() {
  var some = new someClass();
  function unreachable() { some; }
  return function() {};
}

window.f_ = f();

そして、それに対応するスクリーンショット。

どうやらクロージャ(この場合。 function() {} ) は、そのオブジェクトが同じコンテクスト内の他のクロージャによって参照されている場合、そのクロージャ自体が到達可能かどうかに関わらず、すべてのオブジェクトを "alive" 保持します。

私の質問は、他のブラウザ(IE 9+とFirefox)におけるクロージャのガベージコレクションについてです。私は JavaScript ヒープ プロファイラなどの webkit のツールにはかなり詳しいのですが、他のブラウザのツールについてはほとんど知らないので、これをテストすることはできませんでした。

IE9+とFirefoxは、この3つのうちどのケースでガベージコレクションを行うのでしょうか? someClass のインスタンスですか?

解決方法は?

IE9+とFirefoxでテストしてみました。

function f() {
  var some = [];
  while(some.length < 1e6) {
    some.push(some.length);
  }
  function g() { some; } //removing this fixes a massive memory leak
  return function() {};   //or removing this
}

var a = [];
var interval = setInterval(function() {
  var len = a.push(f());
  if(len >= 500) {
    clearInterval(interval);
  }
}, 10);

ライブサイト こちら .

500の配列になることを期待しました。 function() {} を、最小限のメモリで実現しました。

残念ながら、そうはいきませんでした。空の関数はそれぞれ、100万個の数値からなる(永遠に到達できないがGCされていない)配列を保持していたのです。

Chrome は最終的に停止して死に、Firefox は 4GB 近くの RAM を使用した後にすべてを終了し、IE は "メモリ不足" を表示するまで漸近的に遅くなりました。

コメントされている行のどちらかを削除すると、すべてが修正されます。

この3つのブラウザ(Chrome、Firefox、IE)はいずれも、クロージャごとではなく、コンテキストごとに環境記録を保持しているようです。Borisはこの決定の背景にある理由をパフォーマンスだと仮定しており、それはありそうですが、上記の実験に照らしてどの程度パフォーマンスと呼べるかはわかりません。

を参照するクロージャが必要な場合 some (もちろん、ここでは使っていませんが、使ったと想像してください)。

function g() { some; }

を使っています。

var g = (function(some) { return function() { some; }; )(some);

は、クロージャを私の他の関数とは異なるコンテキストに移動させることで、メモリの問題を解決します。

これで、私の人生はもっと面倒なものになる。

追伸:興味本位で、これをJavaで試してみました(関数の中にクラスを定義する機能を使って)。GCは、私がJavascriptに当初期待していたように動作します。