深刻淺出 JavaScript 內存管理,垃圾回收

簡介

本篇文章講解JavaScript 中垃圾回收機制,內存泄漏,結合一些常遇到的例子,相信各位看完後,會對JS 中垃圾回收機制有個深刻的瞭解。node

個人github,歡迎 stargit

內存生命週期

首先,無論什麼程序語言,內存生命週期基本是一致的:github

  • 分配你所須要的內存
  • 使用分配到的內存(讀、寫)
  • 不須要時將其釋放歸還

 在全部語言中第一和第二部分都很清晰。最後一步在低級語言中(C語言等)很清晰,可是在像JavaScript 等高級語言中,這一步是隱藏的、透明的。由於JavaScript 具備自動垃圾收集機制(Garbage collected )。在編寫 JS 時,不須要關心內存使用問題,所需內存分配以及無用內存的回收徹底實現了自動管理。web

內存泄漏

內存泄漏(memory leaks),什麼狀況下回致使內存泄漏?能夠簡單理解爲有些代碼原本要被回收的,但沒有被回收,還一直佔用着操做系統內存,從而越積越多,最終會致使內存泄漏(能夠理解爲,內存滿了,就溢出了)。算法

管理內存(Memory Management)

分配給web瀏覽器的可用內存數量一般要比分配給桌面應用程序少。這樣作的目的主要是處於安全方面考慮,目的是防止運行JS 的網頁耗盡所有系統內存而致使系統崩潰。內存限制問題不只會影響給變量分配內存,同時還會影響調用棧以及在一個線程中可以同時執行的語句數量。chrome

所以,確保佔用最少的內存可讓頁面得到更好的性能。而優化內存佔用的最佳方式,就是爲執行中的代碼只保存必要的數據。一旦數據再也不有用,最好經過將其值設置爲 null 來釋放其引用。這個方法叫作解除引用。這一作法適用於大多數的全局變量和全局對象的屬性。局部變量會在他們離開執行環境時自動被解除引用。瀏覽器

解除一個值的引用並不意味着自動回收改值所佔用的內存。解除引用的真正做用是讓值脫離執行環境,以便垃圾收集器下次運行時將其回收。安全

標記清除(Mark and Sweep)

一般,垃圾收集器(garbage collector)在運行時候會給儲存在內存中的全部變量都加上標記。而後,它會去掉環境中的變量以及被環境中的變量引用的變量的標記。而在此以後再被加上標記的變量將被視爲準備刪除的變量,緣由是環境中的變量已經沒法訪問到這些變量了。最後,垃圾收集器完成內存清除的工做。ide

那標記清除具體是如何呢?有如下幾種算法:函數

  • 在JavaScript 中,全局變量(Global)和window 對象會一直存在,不會被垃圾收集器回收;
  • 遞歸所用到的全部(包括變量和方法),都不會被回收;
  • 全部沒有被標記爲「活躍(active)」的,都會被認爲是垃圾,收集器釋放會回收垃圾,並把內存還給操做系統。

例子:

例一:

var n = 123; 
// 給數值變量分配內存

var s = "azerty"; 
// 給字符串分配內存

// 給對象及其包含的值分配內存
var o = {
  a: 1,
  b: null
};

// 給函數(可調用的對象)分配內存
function f(a){
  return a + 2;
}

例二:

function foo(arg) {
  // 此處bar 是全局變量,window.bar 能夠訪問,因此也不會被回收
  bar = "this is a hidden global variable";
} 

function foo() {
  // 此處this 表明 window
  this.variable = "potential accidental global";
}

例三:

var someResource = getData();
setInterval(function() {
  var node = document.getElementById('Node');
  if(node) {
    node.innerHTML = JSON.stringify(someResource));
  }
}, 1000);

// 上面這段代碼,定時器setInterval 和 someResource 一直存在,不會被回收。能夠改爲下面代碼

var element = document.getElementById('button');

function onClick(event) {
    element.innerHtml = 'text';
}

element.addEventListener('click', onClick);
// 手動移除事件監聽器和變量
element.removeEventListener('click', onClick);
element.parentNode.removeChild(element);

例四:

var intervalId = null, params;

function createChunks() {
  var div, foo, i, str;
  for (i = 0; i < 20; i++) {
    div = document.createElement("div");
    str = new Array(1000000).join('x');
      foo = {
        str: str,
        div: div
      };
      div.foo = foo;
  }
}

function start() {
  if (intervalId) {
    return;
  }
  intervalId = setInterval(createChunks, 1000);
}

function stop() {
  if (intervalId) {
    // 清除定時器
    clearInterval(intervalId);
  }
  // 清除變量
  intervalId = null;
}

連接觀察垃圾回收是怎麼工做的—Google: Watching the GC work

圖片描述

在上面圖片中,能夠觀察到,點擊 start 按鈕,內存和節點數暴增,當點擊stop 時,垃圾收集器回收了這些定時器、變量等,從而釋放了內存。

上期博客

個人github,歡迎star

相關文章
相關標籤/搜索