はじめに
C などの低レベル言語には、malloc() や free() などの低レベルのメモリ管理プリミティブがあります。一方、JavaScript のメモリ プリミティブは、変数 (オブジェクト、文字列など) が作成されるときに割り当てられ、使用されなくなったときに「自動的に」解放されます。後者はガベージコレクションと呼ばれます。この「自動」は混乱を招き、JavaScript (およびその他の高級言語) 開発者にメモリ管理について考える必要がないという錯覚を与えます。
メモリのライフサイクル
どのようなプログラミング言語であっても、メモリのライフサイクルは基本的に同じです:
1. 必要なメモリを割り当てます
2. 使用する (読み取り、書き込み)
3. 使用しないときは放してください ps: 「象を冷蔵庫に入れる」と同じ意味です
プロセスの最初と 2 番目の部分はすべての言語で明確です。低水準言語では最後のステップは明確ですが、JavaScript のような高水準言語では最後のステップが明確ではありません。
JavaScript のメモリ割り当て
変数の初期化
プログラマが割り当てについて心配しないように、JavaScript は変数を定義するときにメモリ割り当てを完了します。
var o = {
a: 1、
b: null
}; // オブジェクトとそれに含まれる変数にメモリを割り当てます
var a = [1, null, "abra"] // 配列とそれに含まれる変数にメモリを割り当てます (オブジェクトと同様)
関数 f(a){
2 を返します;
} // 関数 (呼び出し可能なオブジェクト) にメモリを割り当てます
// 関数式でオブジェクトを割り当てることもできます
someElement.addEventListener('click', function(){
someElement.style.backgroundColor = 'blue';
}, false);
関数呼び出しによるメモリ割り当て
一部の関数呼び出しでは、オブジェクト メモリが割り当てられます:
一部のメソッドは、新しい変数または新しいオブジェクトを割り当てます:
var a = ["ouais ouais", "nan nan"];
var a2 = ["世代", "ナンナン"];
var a3 = a.concat(a2); // 新しい配列には、配列 a と配列 a2 を接続する 4 つの要素があります。
値の使用
値を使用するプロセスは、実際には割り当てられたメモリの読み取りと書き込みの操作です。つまり、変数またはオブジェクトの属性値を書き込んだり、関数パラメーターを渡すこともできます。
メモリが不要になったら解放します
メモリ管理の問題のほとんどはこの段階で発生します。ここで最も難しい作業は、割り当てられたメモリが実際に不要になったことを確認することです。多くの場合、開発者はプログラム内のどのメモリが不要になったかを判断し、それを解放する必要があります。
高級言語インタプリタには「ガベージ コレクター」が組み込まれており、その主な役割はメモリの割り当てと使用状況を追跡することで、割り当てられたメモリが使用されなくなったときに自動的に解放できるようにすることです。特定のメモリが必要かどうかは判断できないため、このプロセスは近似値です。
ガベージコレクション
上で述べたように、一部のメモリが「不要になった」かどうかを自動的に検出するという問題は判断できません。したがって、ガベージ コレクションの実装では、限られた範囲でのみ一般的な問題を解決できます。このセクションでは、主要なガベージ コレクション アルゴリズムとその制限事項を理解するために必要な概念について説明します。
引用
ガベージ コレクション アルゴリズムは主に参照の概念に依存します。メモリ管理のコンテキストでは、オブジェクトが別のオブジェクトに (暗黙的または明示的に) アクセスする権限を持っている場合、そのオブジェクトは別のオブジェクトを参照するオブジェクトと呼ばれます。たとえば、JavaScript オブジェクトには、そのプロトタイプへの参照 (暗黙的な参照) とそのプロパティへの参照 (明示的な参照) があります。
ここで、「オブジェクト」という概念は、特別な Javascript オブジェクトだけでなく、関数スコープ (またはグローバル字句スコープ) も含みます。
参照カウント ガベージ コレクション
これは最も単純なガベージ コレクション アルゴリズムです。このアルゴリズムは、「オブジェクトが不要になったかどうか」の定義を、「オブジェクトがそれを参照している他のオブジェクトを持っているかどうか」として単純化します。オブジェクトを指す参照がない (参照がゼロ) 場合、オブジェクトはガベージ コレクション メカニズムによって回収されます。
たとえば
var o2 = o; // o2 変数は「このオブジェクト」への 2 番目の参照です
o = 1; // ここで、「このオブジェクト」の元の参照 o は o2
var oa = o2.a; // 「このオブジェクト」の a プロパティを参照します
// さて、「このオブジェクト」には 2 つの参照があり、1 つは o2 で、もう 1 つは oa
o2 = "yo" // 元のオブジェクトには参照がありません
// 彼はガベージコレクションされる可能性があります
// ただし、属性 a のオブジェクトはまだ oa によって参照されているため、まだリサイクルできません
oa = null; // a 属性を持つオブジェクトにも参照がありません
// ガベージコレクション可能
制限事項: 循環参照
この単純なアルゴリズムの制限は、あるオブジェクトが別のオブジェクトを参照する (循環参照を形成する) 場合、それらは「不要になった」可能性がありますが、リサイクルされないことです。
"azerty" を返します;
}
f();
// 2 つのオブジェクトが作成され、相互に参照し、ループを形成します
//
が呼び出された後は関数のスコープを離れません。
// したがって、不要になったのでリサイクルできます
// ただし、参照カウント アルゴリズムでは、それらすべてが相互に少なくとも 1 つの参照を持っていることが考慮されるため、リサイクルされません
実践例
IE 6、7 は、DOM オブジェクトに対して参照カウントのリサイクルを実行します。彼らにとってよくある問題はメモリ リークです:
マークアンドクリアアルゴリズム
このアルゴリズムは、「オブジェクトが不要になったかどうか」の定義を「オブジェクトが取得できるかどうか」として簡略化します。
このアルゴリズムは、ルートと呼ばれるオブジェクトのセットアップを前提としています (JavaScript では、ルートはグローバル オブジェクトです)。定期的に、ガベージ コレクターはルートから開始し、ルートから参照されるすべてのオブジェクトを検索し、次にこれらのオブジェクトによって参照されるオブジェクトを検索します。ルートから開始して、ガベージ コレクターは取得できるすべてのオブジェクトとすべてのオブジェクトを検索します。それは入手できないものです。
このアルゴリズムは、「ゼロ参照を持つオブジェクト」は常に取得できないため、前のアルゴリズムよりも優れていますが、その逆は必ずしも真ではありません。「循環参照」を参照してください。
2012 年現在、すべての最新ブラウザはマーク アンド スイープ ガベージ コレクション アルゴリズムを使用しています。 JavaScript ガベージ コレクション アルゴリズムのすべての改善は、マーク スイープ アルゴリズムの改善に基づいており、マーク スイープ アルゴリズム自体や、「オブジェクトが不要になったかどうか」の簡略化された定義は改善されません。
循環参照はもう問題ありません
上記の例では、関数呼び出しが戻った後、グローバル オブジェクトから 2 つのオブジェクトを取得できません。したがって、それらはガベージ コレクターによって収集されます。
2 番目の例では、div とそのイベント ハンドラーがルートから到達できなくなると、ガベージ コレクターによって収集されます。
制限事項: オブジェクトは明示的に到達不能である必要があります
これは制限ですが、それを超えることはほとんどないため、実際にはガベージ コレクション メカニズムを気にする人はほとんどいません。