JavaScriptのメモリ管理とガベージコレクション

Cのような低レベル言語には、一般にmalloc()やfree()などの低レベルのメモリ管理インターフェイスがあります。対照的に、JavaScriptには自動ガベージコレクションメカニズムがあります。実行環境は、コード実行中に使用されるメモリを管理します。メモリは、変数(オブジェクト、文字列など)が作成されると自動的に割り当てられ、使用されない場合は「自動的に」解放されますリリースのプロセスはガベージコレクションと呼ばれます。このガベージコレクションのメカニズムは非常に単純で、使用されていない変数を見つけて、それらが使用しているメモリを解放します。このために、ガベージコレクターは、定期的にこの操作を一定の間隔(またはコード実行中のスケジュールされた収集時間)で実行します。

1.ローカル変数のライフサイクル

ローカル変数は、関数の実行中にのみ存在します。このプロセスでは、対応するスペースがスタック(またはヒープ)メモリに割り当てられ、ローカル変数の値を格納します。次に、これらの変数を関数の実行が終了するまで関数で使用します。終了後、ローカル変数は使用されなくなるため、存在する必要はないので、将来の使用のためにメモリを解放します。

2.記憶のライフサイクル

どのプログラミング言語でも、メモリのライフサイクルは基本的に同じです:

  1. 必要なメモリを割り当てる
  2. 割り当てられたメモリを使用する(読み取り、書き込み)
  3. 必要のないときは解放して戻る
JavaScriptのメモリ割り当て
2.1。値の初期化

プログラマがメモリを割り当てないようにするために、JavaScriptは変数を定義するときにメモリ割り当てを完了します。

var n = 123; // 给数值变量分配内存var s = "azerty"; // 给字符串分配内存
var o = {
  a: 1,
  b: null
};
// 给对象及其包含的值分配内存
// 给数组及其包含的值分配内存(就像对象一样)var a = [1, null, "abra"]; 
function f(a){
  return a + 2;
} 
// 给函数(可调用的对象)分配内存
// 函数表达式也能分配一个对象
someElement.addEventListener('click', function(){
  someElement.style.backgroundColor = 'blue';
}, false);
2.2。関数呼び出しによるメモリの割り当て

一部の関数呼び出しの結果、オブジェクトメモリが割り当てられます。

var d = new Date(); // 分配一个 Date 对象
var e = document.createElement('div'); // 分配一个 DOM 元素

一部のメソッドは、新しい変数または新しいオブジェクトを割り当てます。

// s2 是一个新的字符串
// 因为字符串是不变量
// JavaScript 可能决定不分配内存
// 只是存储了 [0-3] 的范围。
var s = "azerty";
var s2 = s.substr(0, 3); 
// 新数组有四个元素,是 a 连接 a2 的结果
var a = ["ouais ouais", "nan nan"];var a2 = ["generation", "nan nan"];var a3 = a.concat(a2); 
2.3。値を使用する

値を使用するプロセスは、実際には割り当てられたメモリの読み取りと書き込みの操作です。読み取りと書き込みは、オブジェクトの変数または属性値の書き込み、または関数のパラメーターの受け渡しです。

2.4。メモリが不要になった場合は無料

ほとんどのメモリ管理の問題はこの段階にあります。ここで最も難しい作業は、「割り当てられたメモリのうち、本当に必要なくなったメモリ」を見つけることです。多くの場合、開発者はプログラム内のどのメモリブロックが不要になっているかを判断し、解放する必要があります。
高水準言語インタープリターは「ガベージコレクター」を埋め込みます。その主な仕事は、メモリの割り当てと使用を追跡することです。割り当てられたメモリが使用されなくなったときに、メモリは自動的に解放されます。メモリのブロックがまだ必要かどうかを判断することは不可能であるため、これはおおよそのプロセスにすぎません(アルゴリズムでは解決できません)。

3.ガベージコレクション

上記のように、一部のメモリが「不要になった」かどうかを自動的に見つける問題は特定できません。したがって、ガベージコレクションは、制限のある一般的な問題しか解決できません。このセクションでは、必要な概念を説明し、主なガベージコレクションアルゴリズムとその制限について理解します。

3.1。リファレンス

ガベージコレクションアルゴリズムは、主に参照の概念に依存しています。メモリ管理のコンテキストでは、オブジェクトが別のオブジェクトにアクセスする権限(暗黙的または明示的)を持っている場合、1つのオブジェクトが別のオブジェクトを参照していると呼ばれます。たとえば、Javascriptオブジェクトには、プロトタイプへの参照(暗黙的な参照)とその属性への参照(明示的な参照)があります。
ここで、「オブジェクト」の概念は、JavaScriptオブジェクトだけでなく、関数スコープ(またはグローバル字句スコープ)も指します。

3.2。参照カウントガベージコレクション

これは初期のガベージコレクションアルゴリズムです。このアルゴリズムでは、「オブジェクトが不要かどうか」を「オブジェクトが参照するかどうか」と定義しています。オブジェクトへの参照がない場合(ゼロ参照)、オブジェクトはガベージコレクションメカニズムによって収集されます。意味は、各値が参照される回数を追跡することです。変数が宣言され、参照型の値が変数に割り当てられている場合、値が別の変数に割り当てられている場合、引用の価値は1です。回数は+1です。逆に、この値を含む変数に別の値が指定されている場合、値が引用される回数は-1です。この値の値が0の場合、この値を使用する変数がないことを意味します。それが占有していたメモリ空間を再利用します。ガベージコレクターが再度実行されると、参照時間がゼロの値が占めるスペースが解放されます。

// 两个对象被创建,一个作为另一个的属性被引用,另一个被分配给变量o
// 很显然,没有一个可以被垃圾收集
var o = { 
  a: {
    b:2
  }}; 
  
// o2变量是第二个对“这个对象”的引用
var o2 = o; 

// 现在,“这个对象”只有一个o2变量的引用了,“这个对象”的原始引用o已经没有
o = 1; 

// 引用“这个对象”的a属性
// 现在,“这个对象”有两个引用了,一个是o2,一个是oa
var oa = o2.a; 
               
// 虽然最初的对象现在已经是零引用了,可以被垃圾回收了
// 但是它的属性a的对象还在被oa引用,所以还不能回收
o2 = "yo"; 

// a属性的那个对象现在也是零引用了
// 它可以被垃圾回收了
oa = null; 
3.2.1。制限:循環参照

Netspace Navigator3.0は最初に参照カウントを使用しましたが、すぐに深刻な問題が見つかりました。循環参照
循環参照は、オブジェクトAのオブジェクトBへのポインターとオブジェクトBのオブジェクトAへのポインターを参照します。
アルゴリズムには制限があります。循環参照を処理できません。次の例では、2つのオブジェクトが作成され、相互に参照されて、サイクルを形成しています。それらが呼び出された後、それらは関数のスコープを離れるので、それらはもはや有用ではなく、リサイクルすることができます。ただし、参照カウントアルゴリズムでは、すべてが少なくとも1回は相互に参照していると見なされるため、再利用されません。

function f(){
  var objectA = new Object();
  var objectB = new Object();
  objectA .a = objectB ; // o 引用 o2
  objectB .a = objectA ; // o2 引用 o
}
f();

この例では、oAとoBは相互に参照します。つまり、両方のオブジェクトへの参照の数は2です。カウント戦略を使用した後、oAとoBは関数の実行後も存在し続けます。この関数が複数回呼び出された場合、参照の数が0になることはないため、大量のメモリはリサイクルされませんが、マーキング戦略は問題になりません

3.2.2。実用的な例

IE 6、7は、参照カウントを使用してDOMオブジェクトをガベージコレクションします。このメソッドは、オブジェクトが循環的に参照されている場合にメモリリークを引き起こすことがよくあります。

var div;
window.onload = function(){
  div = document.getElementById("myDivElement");
  div.circularReference = div;
  div.lotsOfData = new Array(10000).join("*");
};

上記の例では、myDivElement DOM要素のcircularReference属性がmyDivElementを参照しているため、循環参照になります。この属性が明示的に削除されていないかnullに設定されている場合、参照カウントガベージコレクターは常に少なくとも1つの参照を持ち、DOMツリーから削除された場合でもDOM要素をメモリに保持します。このDOM要素に大量のデータ(上記のlotOfData属性)がある場合、このデータによって占有されているメモリは解放されません。

3.3。マークスイープアルゴリズム

このアルゴリズムは、単に「オブジェクトが不要かどうか」を「オブジェクトが使用可能かどうか」と定義しています。
環境への変数は、この変数は、「環境への」としてマークされた場合、一般的に限り、対応する環境に実行フローとして、あなたはそれを使用するかもしれない、変数のメモリを占有し、環境中に放出することはできません。変数が環境を離れるときに、「環境を離れる」というマークを付けます。

3.3.1マーキング方法:

変数にマークを付けるには、任意の方法を使用できます。たとえば、変数が環境に入るときに特別なビットを反転して記録したり、「環境に入る」という変数リストと「環境から出る」という変数リストを使用して、どの変数を追跡したりできます。変わりました。ただし、これは重要ではありません。重要なのは採用した戦略にあります。

ガベージコレクターは、実行時にメモリに格納されているすべての変数にマークを付け、環境内の変数のタグと環境内の変数によって参照されている変数を削除します。この時間以降にマークされた変数は、環境内の変数がこれらの変数にアクセスできなくなるため、削除される変数として扱われます。最後に、ガベージコレクターはメモリの消去作業を完了し、マークされた値を破棄して、それらが占有していたメモリ空間を再利用します。

このアルゴリズムは、ルート(Javascriptではルートはグローバルオブジェクト)と呼ばれるオブジェクトが設定されていることを前提としています。ガベージコレクターは定期的にルートから開始し、ルートから参照されるすべてのオブジェクトを検索してから、これらのオブジェクトによって参照されるオブジェクトを検索します。ルートから、ガベージコレクターは取得できるすべてのオブジェクトを検索し、取得できないすべてのオブジェクトを収集します。

「ゼロ参照を持つオブジェクト」は常に利用できないため、このアルゴリズムは以前のアルゴリズムよりも優れていますが、その逆は必ずしも当てはまりません。「循環参照」を参照してください。
2012年以降、すべての最新のブラウザーは、マークアンドスイープガベージコレクションアルゴリズムを使用しています。JavaScriptガベージコレクションアルゴリズムに対するすべての改善は、マークアンドスイープアルゴリズムの改善に基づいており、マークアンドスイープアルゴリズム自体の改善はなく、「オブジェクトが不要になったかどうか」の単純化された定義もありません。

3.3.2循環参照は問題ではなくなりました

上記の例では、関数呼び出しが戻った後、2つのオブジェクトをグローバルオブジェクトから取得できません。したがって、それらはガベージコレクターによってリサイクルされます。2番目の例も同じで、divとそのイベント処理がルートから取得できなくなると、ガベージコレクターによって収集されます。

3.3.3制限:ルートオブジェクトからクエリできないオブジェクトはクリアされます

これは制限事項ですが、実際には同じような状況に遭遇することはめったにないため、開発者はガベージコレクションのメカニズムについてそれほど心配していません。

4.パフォーマンスの問題

すべてのブラウザーで、ガベージコレクションプロセスをトリガーできますが、お勧めできません。IEでは、window.CollectGarbage()メソッドを呼び出すと、ガベージコレクションがすぐに実行されます。Opera 7以降では、window.opera.collect()を呼び出すとガベージコレクションルーチンも開始されます。

5.メモリ管理

JavaScriptは通常、デスクトップアプリケーションよりも、Webブラウザーに割り当てるメモリを少なく割り当てます。これはセキュリティ上の理由で行われ、目的はJavaScript Webページがすべてのシステムメモリを使い果たしてシステムがクラッシュするのを防ぐことです。メモリ制限の問題は、変数へのメモリの割り当てに影響するだけでなく、コールスタックやスレッドで同時に実行できるステートメントの数にも影響します。
最小限のメモリを使用することで、ページのパフォーマンスが向上します。メモリ使用量を最適化する最良の方法は、実行中のコードに必要なデータのみを保存することです。データが役に立たなくなったら、nullに設定して解放するのが最善です。この方法は逆参照と呼ばれます。このアプローチは、ほとんどのグローバル変数とグローバルオブジェクトのプロパティに適用されます。ローカル変数は、実行環境を離れると自動的に逆参照されます。

funtion createPerson(name){
  var localPerson = new Object();
  localPerson.name = name;
  return localPerson;
} 
Var globalPerson = createPerson(‘Nicholas’);
// 手工解除globalPerson 的引用
globalPerson = null;

値を逆参照しても、その値によって占有されていたメモリが自動的に解放されるわけではありません。逆参照の実際の効果は、実行環境から値を取得して、ガベージコレクターが次回実行したときに値をリサイクルできるようにすることです。

参照:
[1]:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Memory_Management
[2]:JavaScriptの高度なプログラミング(第3版)78-81

元の記事を5件公開しました Likes0 訪問数125

おすすめ

転載: blog.csdn.net/forteenBrother/article/details/105352944
おすすめ