Memory Management

by 1 contributor:

This article is in need of an editorial review.

This translation is incomplete. Please help translate this article from English.

紹介

Cのような低レベル言語は、malloc()free()のような底レベルメモリ管理プリミティブを持ちます。他方では、JavaScriptの値は、もの(オブジェクト、文字列など)が生成されるときに割り当てられます。そして、"自動的に"使用されないとき開放されます。後者のプロセスがガーベジコレクションを呼び出します。この"自動的"は混乱の源であり、JavaScript (そして、高レベル言語) 開発者に彼らは、メモリ管理を気にしないことを決定することができる印象を与えます。これは誤りです。

メモリライフサイクル

プログラミング言語に関係なく、メモリのライフサイクルはかなり常に同じです:

  1. あなたが必要とするメモリを割り当てます
  2. 使用します (読み, 書き)
  3. それはもはや必要ないときに割り当てられたメモリを解放します

第1および第2の部分は、すべての言語で明示されています。最後の部分は、低レベルの言語で明示的であるが、JavaScriptのような高水準言語で、主に暗黙的です。

JavaScriptでの割当

値の初期化

割り当てでプログラマを悩まさないために、JavaScriptが値を宣言するのと並んで値の初期化をしません。

var n = 123; // allocates memory for a number
var s = "azerty"; // allocates memory for a string 

var o = {
  a: 1,
  b: null
}; // allocates memory for an object and contained values

// (like object) allocates memory for the array and 
// contained values
var a = [1, null, "abra"]; 

function f(a){
  return a + 2;
} // allocates a function (which is a callable object)

// function expressions also allocate an object
someElement.addEventListener('click', function(){
  someElement.style.backgroundColor = 'blue';
}, false);

関数呼び出しを介して割り当て

いくつかの関数呼び出しは、オブジェクトの割り当てをもたらします。

var d = new Date(); // allocates a Date object

var e = document.createElement('div'); // allocates a DOM element

いくつかのメソッドは、新しい値またはオブジェクトを割り当てます:

var s = "azerty";
var s2 = s.substr(0, 3); // s2 is a new string
// Since strings are immutable value, 
// JavaScript may decide to not allocate memory, 
// but just store the [0, 3] range.

var a = ["ouais ouais", "nan nan"];
var a2 = ["generation", "nan nan"];
var a3 = a.concat(a2); 
// new array with 4 elements being
// the concatenation of a and a2 elements

値の使用

値を使用することは、基本的に割り当てられたメモリで読み書きすることを意味します。これは変数やオブジェクトの値を読み書きすることや引数を関数に渡すことによって行われます。

メモリが不要になったときに解放

メモリ管理の問題のほとんどは、この段階に来ます。ここで最も難しい作業では、「割り当てられたメモリは、もはや必要とされていない」ときを発見することです。開発者がプログラム内でそのようなピースのメモリはもう必要がなく開放するかどうかを決めるのはしばしば必要です。

高レベル言語は、仕事がメモリ割当てを追跡することである「ガベージコレクタ」と呼ばれるソフトウェアを埋め込み、割り当てられたメモリの一部がもはや、その場合に必要とされていないときに見つけるために使用し、それが自動的に解放されます。メモリの一部のピースが必要とされているかどうかを知ることの一般的な問題はundecidableであるため、このプロセスは近似値です(アルゴリズムによって解決できません)。

ガーベジコレクション

自動的にいくつかのメモリは"もはや必要ない"かどうかを見つけることの一般的な問題上述したように決定不能です。その結果、ガベージコレクションは、一般的な問題の解決策の制限を実装しています。このセクションでは、メインのガベージコレクションのアルゴリズムとその限界を理解するために必要な概念を説明します。

リファレンス

ガーベジコレクションアルゴリズムが依存している主な概念は、referenceの概念です。メモリ管理のコンテキスト内で、オブジェクトは別のオブジェクトを参照するように、前者は後者へのアクセス権を(暗黙的または明示的)に持っている場合、言われています。例えば、JavaScript オブジェクトはprototypeとプロパティ値への参照を持ちます。(明示的な参照).

これに関連して、"オブジェクト"の概念は通常のJavaScriptオブジェクトよりも広いものに拡張され、また、関数のスコープが含まれています(または、グローバルレキシカルスコープ)。

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

これは、最も素朴なガベージコレクションアルゴリズムです。このアルゴリズムは、"オブジェクトがそれに参照する他のオブジェクトを持っていない"に"オブジェクトはもう必要ない”の定義を低減します。このオブジェクトでのゼロ基準ポインティングがある場合、オブジェクトはガベージコレクションの対象と考えられています。

var o = { 
  a: {
    b:2
  }
}; 
// 2 objects are created. One is referenced by the other as one of its property.
// The other is referenced by virtue of being assigned to the 'o' variable.
// Obviously, none can be garbage-collected


var o2 = o; // the 'o2' variable is the second thing that 
            // has a reference to the object
o = 1;      // now, the object that was originally in 'o' has a unique reference
            // embodied by the 'o2' variable

var oa = o2.a; // reference to 'a' property of the object.
               // This object has now 2 references: one as a property, 
               // the other as the 'oa' variable

o2 = "yo"; // The object that was originally in 'o' has now zero
           // references to it. It can be garbage-collected.
           // However what was its 'a' property is still referenced by 
           // the 'oa' variable, so it cannot be free'd

oa = null; // what was the 'a' property of the object originally in o 
           // has zero references to it. It can be garbage collected.

制限: サイクル

それがサイクルになるとき制限があります。次の例では、2つのオブジェクトが作成され、相互に参照されています。 – このようにして、サイクルを生成します。これらは、関数呼び出しの後に関数スコープの外に取得することはできません。だから、効果的に役に立たなく、開放することができました。しかし、参照カウントアルゴリズムは、両方のオブジェクトのそれぞれが少なくとも一回参照されているので、どれもガベージコレクトすることができないことを考えています。

function f(){
  var o = {};
  var o2 = {};
  o.a = o2; // o references o2
  o2.a = o; // o2 references o

  return "azerty";
}

f();

Real-life example

Internet Explorerの6および7は、DOMオブジェクトの参照カウントは、ガベージコレクタを有することが知られています。サイクルは、メモリリークを発生させることができるよくある間違いです:

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

上記の例では、DOM要素"myDivElement"は"circularReference"プロパティに自身への循環参照を持っています。そのプロパティが明示的に削除またはヌルにされていない場合, 参照カウントのガベージコレクタは、常に少なくとも1つの参照そのまま有することになり、それがDOMツリーから削除された場合でも、メモリ内のDOM要素を維持します。DOM要素は、大量のデータを保持している場合("lotsOfData"プロパティで上記の例に示されています)、このデータによって消費されるメモリが解放されることはありません。

マーク·アンド·スイープアルゴリズム

このアルゴリズムは、"オブジェクトが到達不能である"と"オブジェクトはもう必要ない"の定義を低減します。

このアルゴリズムは、rootsと呼ばれるオブジェクトのセットについての知識を前提としています(JavaScriptでは、rootはグローバルオブジェクトです)。定期的に、ガベージコレクタは、これらのrootから開始し、これらのrootから参照されるすべてのオブジェクト、それから、これらの中から参照されるすべてのオブジェクトなどを見つけます。rootから開始すると、ガベージコレクタは、すべてのreachableオブジェクトを見つけ、すべての非到達可能オブジェクトを集めます。

"オブジェクトはゼロ基準を持っている"はこのオブジェクトが到達不能であることにつながるので、このアルゴリズムは、前のものよりも優れています。サイクルで見てきたように逆は真ではありません。

2012年の時点で、すべての近代的なブラウザでは、マークアンドスイープガベージコレクタをリリースします。過去数年間でJavaScriptのガベージコレクション(世代別/インクリメンタル/並行/並列ガベージコレクション)の分野で行われたすべての改善は、ガベージコレクションアルゴリズム自体に対する改善も"もうオブジェクトが必要とされていない"ときの定義のその減少も除いて、このアルゴリズムの実装の改善であります。

サイクルはもはや問題ではありません

最初の上記の例では、関数呼び出しが戻った後、2つのオブジェクトは、グローバルオブジェクトから到達可能な何かによってもはや参照されません。その結果、それらは、ガベージコレクタによって到達不能を検出されます。

同じことは、第二の例に付随します。divとそのハンドラがrootsから到達不能になったら、それらは両方ともお互いを参照するにもかかわらずガベージコレクトことができます。

制限: 明示的に到達不能にされる必要があるオブジェクト

これは制限としてマークされていますが、誰もが通常のガベージコレクションについてそれほど気にしない理由があるのは、実際にはめったに達していないものです。

関連情報

ドキュメントのタグと貢献者

タグ: 
Contributors to this page: shide55
最終更新者: shide55,
サイドバーを隠す