從V8的內存管理算法出發-教你如何管理內存

@[toc]java

導語

  • 什麼是V8?

- V8 js運行的引擎(相似 java運行在jvm上)node

  • 爲何要關注內存?

- 防止頁面佔用內存過大,引發客戶端卡頓,甚至無響應。c++

- Node 使用的也是V8,內存對於後端服務的性能相當重要,由於服務的持久性,後端更容易形成內存溢出。面試

- 面試裝逼神器。算法

1、V8引擎如何回收垃圾

一、V8的內存分配

在這裏插入圖片描述

(1)內存大小

內存的大小和操做系統有關,64位爲1.4G,32位爲0.7G。後端

  • 64位下新生代的空間爲 64MB ,老生代爲 1400MB。
  • 32位下新生代的空間爲 16MB ,老生代爲 700MB。

    - 爲何只設置 1.4G ? 而不是2G、3G...數組

    - 一、js最初設計是在瀏覽器上跑的,瀏覽器上的js不持久,運行完代碼就能夠了,因此 1.4G 徹底夠用。瀏覽器

    - 二、js有垃圾回收機制,回收時會暫停全部代碼的執行,(回收300MB大概須要0.5s),若是設置爲3G,那回收時間會特別長,程序中止時間太久。markdown

(2)新生代和老生代

  • 新生代(semi space From & semi space To)簡單地說就是==複製==
  • 老生代簡單地說就是==標記刪除整理==

新生代:閉包

  • 新生代存放的是 存活時間比較短的變量,會頻繁發生垃圾回收。
  • 新生代會標記活着的變量。首先會將 From 中活着的變量 複製到 To 中,而後將 From 清空,下一次會將 To 中活着的變量 複製到 From 中,並將 To 清空。
  • 這是典型的 ==犧牲空間,獲取時間== 算法
  • 由於作清空是很快的,而一個一個刪除而後整理是很慢的(內存是連着的,刪除一個變量,就須要將後面的變量向前複製,而後刪除原來的,整理很慢)

老生代:

  • 老生代會標記死掉的變量,作刪除、整理(碎片整理)的操做。
  • 數組是須要連續的內存空間,整理很耗時間
    在這裏插入圖片描述

晉升機制:

  • 剛開始定義的變量都是新生代,當變量在新生代經歷過一次回收,就擁有資格晉升爲老生代(但不會直接放入老生代)。
  • 直到新生代 To(From) 空間已經使用超過 20% ,那麼就會晉升到老生代空間。

二、變量處理

  • 內存主要就是存儲變量等數據的
  • 局部變量當程序執行結束,且沒有引用的時候就會死掉
  • 全局對象會始終存活到程序運行結束

eg:

function f () {
    var a = ''
}
f(); // f 執行結束 a 就會被回收複製代碼
function f () {
    var a = '';
    return a;
}
var b = f(); // f 執行結束 a 不會被回收,由於外層做用域還有 a 的引用 b。複製代碼

2、如何查看V8內存使用狀況

一、使用 node 來查看內存使用狀況

  • 經過 process.memoryUsage()
    在這裏插入圖片描述
    在這裏插入圖片描述
  • rss: V8申請到的總佔用空間
  • heapTotal: 堆總內存
  • heapUsed: 已使用的內存
  • external: node專有(底層是c,額外申請到的c++內存 )

二、在 chorme 瀏覽器中查看內存使用狀況

  • 經過 window.performance
    在這裏插入圖片描述

---

3、內存優化實例

一、優化內存技巧

  • (1)儘可能不要定義全局變量
  • (2)全局變量記得銷燬掉

- a = undefined

- delete a;不建議使用,嚴格模式下會出問題

  • (3)用匿名自執行函數變全局爲局部

- (function () {}())

eg:

function getme () {
  var mem = process.memoryUsage();
  var format = function (bytes) {
    return (bytes / 1024 / 1024).toFixed(2) + 'MB';
  }
  console.log('heapTotal: ' + format(mem.heapTotal) + 'heapUsed: ' + format(mem.heapUsed));
}
var size = 20 * 1024 * 1024;
var arr1 = new Array(size);
var arr2 = new Array(size);
var arr3 = new Array(size);
var arr4 = new Array(size);
var arr5 = new Array(size);
var arr6 = new Array(size);
var arr7 = new Array(size);
var arr8 = new Array(size);
var arr9 = new Array(size);
getme(); // 內存溢出 極限 8 個複製代碼

在這裏插入圖片描述

function getme () {
  var mem = process.memoryUsage();
  var format = function (bytes) {
    return (bytes / 1024 / 1024).toFixed(2) + 'MB';
  }
  console.log('heapTotal: ' + format(mem.heapTotal) + 'heapUsed: ' + format(mem.heapUsed));
}
var size = 20 * 1024 * 1024;

function a () {
  var arr1 = new Array(size);
  var arr2 = new Array(size);
  var arr3 = new Array(size);
  var arr4 = new Array(size);
  var arr5 = new Array(size);
  var arr6 = new Array(size);
  var arr7 = new Array(size);
  var arr8 = new Array(size);
}
a();
var arr9 = new Array(size);
getme(); // 內存不會溢出複製代碼

二、關於閉包和內存使用

首先重要的是:==閉包並不會影響內存==雖然在某一版(好久遠了)的 《Javascript 權威指南》中做者說過閉包會佔用內存,讓儘可能避免閉包。由於當時 IE5 存在這個 BUG ,因此才致使這個問題。如今的V8是沒有這個問題的,因此這是錯誤的說法。

eg:

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

for (let i = 10000; i < 10100; i++) {
  setTimeout(function () {
    console.log(i);
    getme(); // 內存佔用 4.x MB
  })
}

// 閉包形式
for (let i = 10000; i < 10100; i++) {
  (function (i) {
    setTimeout(function () {
      console.log(i);
      getme(); // 內存佔用 4.x MB
    })
  })(i)
}複製代碼

碼字不易,以爲有幫助的小夥伴點個贊支持下~

在這裏插入圖片描述

掃描上方二維碼關注個人訂閱號~

以爲有幫助的小夥伴點個贊支持一下~

相關文章
相關標籤/搜索