1. 程式人生 > 實用技巧 >JS記憶體

JS記憶體

  • 記憶體是用來存什麼的

通俗的來說呢,就是用來存 var let function const 宣告的變數。

  • 記憶體的大小

與作業系統有關,64位1.4G 32位0.7G。

  • 為啥記憶體大小要這麼設計,為啥不是越大越好

1.表象原因,1.4G夠用了 JS設計之初是作為指令碼語言(一次性的執行,執行完畢就直接釋放),相對於java,C這些 編寫永續性的服務語言(記憶體一般不受限制)來說,夠用了,想想你一次性的定義變數超過1.4G還是有難度的(當然不要耍賴用迴圈)。

2.深層次的原因,JS每回收一次垃圾,會把整個程式碼的執行暫停, 回收200MB大概需要30ms,如果記憶體設計的太大,回收垃圾的時候回暫停很久,使用者體驗不好。

  • V8 的記憶體分配

新生代會頻繁發生變數的移動,老生代存的 比較久。

64位:新生代64MB 老生代1400MB

32位:新生代16MB 老生代700MB

  • 新生代記憶體

存放生存的並不久的變數

  • 老生代記憶體

存放常駐變數

  • 變數從新生代 --> 老生代

1.新生代記憶體空間使用超過了25%(前置條件)。

2.經過了一次垃圾回收,但是還沒有回收掉的變數(還有地方會用到的變數),使其變為常駐變數。

var a = (()=>{
    var b = '1';
    return ()=>{
    return b;
}
});
  • 新生代回收演算法

為什麼分為兩部分,因為新生代會頻繁發生變數的移動,一開始變數都放在from, 比如a, b, c三個變數,第一次回收以後,比如a, c還活著,那就只需要把a, c放到to中,然後把from中的全部刪除,同樣的道理,下一次回收從to中吧活著的變數複製到from, 刪除to中的。只需要做兩步 複製 刪除,這樣比較高效。

  • 老生代回收演算法

標記 清除 整理

先給需要回收的變數加標記,然後執行刪除,刪除之後呢,刪除變數的位置會產生磁碟碎片,舉個例子:陣列(只能儲存相同大小的同類型變數,在記憶體上必須是連續的空間) [1, ,3, ] 還有兩個位置,但是如果我們現在要把[2,4]存進去,是存不進去的。所以還需要進行一步整理磁碟粹片。

  • 記憶體如何回收

記憶體快接近滿時,如果是全域性變數,沒有執行完畢不會回收,區域性變數失去引用回收。

下面是一個例子,全域性的arr,我們不停往裡面push大陣列,因為arr是全域性的,不能被回收記憶體就爆了。

function printMe(){
    var mem = process.memoryUsage();
    var format = function(bytes){
        return (bytes/1024/1024).toFixed(0) + 'MB';
    };
    console.log('ToTal:' + format(mem.heapTotal) + 'Used:' + format(mem.heapUsed));
}

var arr = [];
var size = 30 * 1024 * 1024;
for (let i = 0; i < 15; i++) {
    arr.push(new Array(size));
    printMe();
}

在通過一個小例子來看下JS回收臨時變數的過程:

function printMe(){
    var mem = process.memoryUsage();
    var format = function(bytes){
        return (bytes/1024/1024).toFixed(0) + 'MB';
    };
    console.log('ToTal:' + format(mem.heapTotal) + 'Used:' + format(mem.heapUsed));
}

var arr = [];
var size = 30 * 1024 * 1024;
function notGolbal() {
    var noarr = []; // 臨時
    for (let i = 0; i < 3; i++) {
        noarr.push(new Array(size));
    }
}
notGolbal();
setInterval(()=>{
    arr.push(new Array(size));
    printMe();
}, 1000);

看圖中圈出部分,已經進行了臨時變數的回收。

  • 開發應該注意些什麼

1.能不定義為全域性變數,就不要定義為全域性變數,非得定義為全域性變數,用完記得手動回收(設定值為null/undefined)。

2.如果用記憶體實現快取要做限制,如果超過這個限制(先進先出),就把最開始的快取清空,防止記憶體爆滿。

3.上傳大檔案時,避免直接操作整個檔案,(上傳檔案其實就是把檔案先從硬碟讀取到記憶體中,再從記憶體進行上傳到伺服器的操作,如果檔案過大,記憶體可能扛不住),把檔案切片上傳。

  • 效能監控方案

1.Lighthouse -谷歌推出的,可直接在瀏覽器安裝(需要翻牆),也可以通過npm安裝,通過命令列來對網站的效能做測試。比如以下命令的意思就是檢測https://study.163.com/的效能,將結果輸出為HTML,路徑為當前命令列執行的路徑。

lighthouse https://study.163.com/ --output=html -path ./

2.對window.performance的資料進行分析,然後自行優化。