MDN wants to learn about developers like you: https://qsurvey.mozilla.com/s3/MDN-dev-survey

翻譯不完整。請協助 翻譯此英文文件

介紹

像 C 語言一樣低階的語言,都有著如 malloc() 跟 free() 的基本函數來做記憶體控管。另一方面,當 Javascript 建立事物(如物件、字串等)時會分配空間給值且自動釋放不再使用的值。後者的流程稱作為回收機制。這個自動化的回收流程是一個混亂的根源,且給予Javascript (或其他高階語言) 的開發者以為可以決定不須理會記憶體控管的想法。這是一個錯誤的想法。

記憶體生命週期

不論是哪種程式語言,記憶體生命週期(運作方式)幾乎總是一樣:

  1. 配置你程式需要的記憶體空間
  2. 使用配置到的記憶體空間(讀,寫)
  3. 當不再使用時釋放已被配置的記憶體空間

在所有語言中,第二點的(運作方式)是確定的。第一點以及最後一點在低階語言中是確定的,但是在高階語言如Javascript 則通常是不明確的。

Javascript 的記憶體配置

值的初始化

為了不讓開發者對配置感到困擾,Javascript 會在宣告值的同時完成記憶體配置

var n = 123; // 配置一記憶體空間給數字
var s = 'azerty'; // 配置記憶體空間給字串 

var o = {
  a: 1,
  b: null
}; //配置記憶體空間給內含值的物件

// (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(); // 配置一個日期物件

var e = document.createElement('div'); // 配置一個 DOM 物件

有些方法配置新的值或物件:

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

值的使用

基本上使用值表示對已被配置的記憶體做讀寫。可藉由讀取或寫入變數的值或一個物件特性或甚至傳一個參數到函數中來完成此事。

釋放不再使用的記憶體

談論到記憶體管理,問題通常出現在這個階段。最困難的工作是尋找「已不再被使用的記憶體配置空間」。通常程式要求開發者來決定程式裡面的這塊記憶體空間不再使用且釋放該空間回給系統。

High-level languages embed a piece of software called "garbage collector" whose job is to track memory allocation and use in order to find when a piece of allocated memory is not needed any longer in which case, it will automatically free it. This process is an approximation since the general problem of knowing whether some piece of memory is needed is undecidable (can't be solved by an algorithm).

回收機制

As stated above, the general problem of automatically finding whether some memory "is not needed anymore" is undecidable. As a consequence, garbage collections implement a restriction of a solution to the general problem. This section will explain the necessary notions to understand the main garbage collection algorithms and their limitations.

參考

The main notion garbage collection algorithms rely on is the notion of reference. Within the context of memory management, an object is said to reference another object if the former has an access to the latter (either implicitly or explicitly). For instance, a JavaScript object has a reference to its prototype (implicit reference) and to its properties values (explicit reference).

In this context, the notion of "object" is extended to something broader than regular JavaScript objects and also contains function scopes (or the global lexical scope).

垃圾回收參考計數

這是一個最務實的垃圾回收演算法。 This algorithm reduces the definition of "an object is not needed anymore" to "an object has no other object referencing to it". An object is considered garbage collectable if there is zero reference pointing at this object.

Example

var o = { 
  a: {
    b: 2
  }
}; 
// 2 objects are created. One is referenced by the other as one of its properties.
// 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 freed

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

Limitation: cycles

There is a limitation when it comes to cycles. In the following example two objects are created and reference one another, thus creating a cycle. They will go out of scope after the function call, so they are effectively useless and could be freed. However, the reference-counting algorithm considers that since each of the two objects is referenced at least once, neither can be garbage-collected.

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

  return 'azerty';
}

f();

真實案例

Internet Explorer 6 and 7 are known to have reference-counting garbage collectors for DOM objects. Cycles are a common mistake that can generate memory leaks:

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

In the above example, the DOM element "myDivElement" has a circular reference to itself in the "circularReference" property. If the property is not explicitly removed or nulled, a reference-counting garbage collector will always have at least one reference intact and will keep the DOM element in memory even if it was removed from the DOM tree. If the DOM element holds lots of data (illustrated in the above example with the "lotsOfData" property), the memory consumed by this data will never be released.

Mark-and-sweep algorithm

This algorithm reduces the definition of "an object is not needed anymore" to "an object is unreachable".

This algorithm assumes the knowledge of a set of objects called roots (In JavaScript, the root is the global object). Periodically, the garbage-collector will start from these roots, find all objects that are referenced from these roots, then all objects referenced from these, etc. Starting from the roots, the garbage collector will thus find all reachable objects and collect all non-reachable objects.

This algorithm is better than the previous one since "an object has zero reference" leads to this object being unreachable. The opposite is not true as we have seen with cycles.

As of 2012, all modern browsers ship a mark-and-sweep garbage-collector. All improvements made in the field of JavaScript garbage collection (generational/incremental/concurrent/parallel garbage collection) over the last few years are implementation improvements of this algorithm, but not improvements over the garbage collection algorithm itself nor its reduction of the definition of when "an object is not needed anymore".

Cycles are not a problem anymore

In the first above example, after the function call returns, the 2 objects are not referenced anymore by something reachable from the global object. Consequently, they will be found unreachable by the garbage collector.

The same thing goes with the second example. Once the div and its handler are made unreachable from the roots, they can both be garbage-collected despite referencing each other.

Limitation: objects need to be made explicitly unreachable

Although this is marked as a limitation, it is one that is rarely reached in practice which is why no one usually cares that much about garbage collection.

可以參考

文件標籤與貢獻者

 此頁面的貢獻者: mybaseball52
 最近更新: mybaseball52,