內存優化+運行性能優化

在我前面的文章中,有寫到 V8 的內存管理機制,其中 就有 標記整理 算法

但是如果有同學 對js 語言底層的實現 深入瞭解的話,肯定會疑惑,爲什麼需要整理內存,獲得一個大的連續空間呢?

  1. js 是一個 萬物皆對象的語言,functon,number,string 等等都是對象,就連 Array 都是對象
  2. 而理論上來說,一個對象是 hash 表結構存儲數據的
  3. 而hash 表結構存儲數據的話,就意味着 他其實不需要 多麼大的連續空間,就能夠存儲很多很多的數據

所以說,這裏需要更加深入地去了解 V8 內部到底是怎麼處理 一個 對象數據的

1、一個普通的對象內部存儲方式

v8引擎 爲了增加讀取數據的性能考慮,其實做了很多很多的優化

  1. 在一個普通的對象之中,存放屬性分爲 兩個區域,一個叫做快區域 名字叫做 elements ,一個叫做慢區域 叫做 properties
  2. 快區域是使用 連續內存進行存儲的,而慢區域則是 使用hash 表結構存儲的
  3. 當讀取一個屬性的時候,v8 先是在 快區域中尋找,沒找到纔會去 慢區域尋找
  4. 如下面的內容所示,只要是 數字類型的,就被放在了 elements 裏面,而 其他類型的,被放在了 properties 裏面
  5. 而數組類型的 ,自然在大多數情況下都被放在了 elements 裏面,也就是說,在 js 裏,其實數組 還真是被放在連續內存裏面的
        function FS(fast, slow) {
            this.fast2slow = {}
            for (let i = 0; i < fast; i ++) {
                this[i] = 'fast'
            }
            for (let i = 0; i < slow; i++) {
                this[`slow${i}`] = slow
            }
        }
        var fs = new FS(10, 10)

在某些文章中,有寫到 當 數字大到一定程度的時候,數組 的存儲結構也會變成一個 hash 表

        function FS1(fast, slow) {
            this.fast2slow = new Array(10)
        }
        function FS2(fast, slow) {
            this.fast2slow = new Array(100000000)
        }
        var fs1 = new FS1()
        var fs2 = new FS2()

  1. 在 fs1 中,可以很明顯的看到 elements 裏面已經沒有再存儲數據了
  2. 在 fs2 中,則還有 數據在存儲

2、隱藏類

  1. 衆所周知的是,js 是一門 動態語言類型,不像 java 語言那樣,有着靜態類型
  2. 但是 在這方面 就會出現一個問題,那就是讀取數據的時候不如靜態語言快速
  3. 靜態語言中的 類 會對 某一個屬性 進行規定,然後快速地查找到這個值,而不是像 動態語言一樣,需要遍歷一邊所有的屬性因爲 V8 引擎不知道 開發者內部到底有沒有這個類
  4. 那麼爲了這個,V8 引擎爲此又做出了什麼優化呢?
  1. 在一個對象創建了之後,V8 引擎會爲其分配一個 隱藏類
  2. 就像是 靜態語言的類一樣,可以幫助 V8引擎 快速地定位到一個元素的所在
  3. 在類似的數據結構中,V8 引擎會爲其分配相同的類,也就是 相同的屬性名稱,相同的屬性數量
  4. 有了隱藏類之後,那麼當 V8 訪問某個對象中的某個屬性時,就會先去隱藏類中查找該屬性相對於它的對象的偏移量,有了偏移量和屬性類型,V8 就可以直接去內存中取出對於的屬性值,而不需要經歷一系列的查找過程,那麼這就大大提升了 V8 查找對象的效率

3、優化

所以瞭解了這個之後,又有什麼用呢?

  1. 使用 typeScript 確實在某種意義上能夠幫助我們加快 js 的運行速度
  2. 不要隨便地使用 delete 或者 增加一個屬性,因爲這樣會導致 隱藏類失效而 重新 構建一個新的隱藏類
  3. 使用字面量初始化對象時,要保證屬性的順序是一致的,
  4. 例如 var a = {x: 1, y: 2}; b = {y: 1, x : 2} 這樣會導致 創建兩個 隱藏類

學習自 李兵 老師的 《圖解 Google V8》

4、其他的優化方案

  1. 慎用全局變量,因爲會順着作用域鏈查找變量,查找的層級越高,速度就越慢
  2. 緩存全局變量,例如: 在 一個函數內部,如果需要調用 document.createElement 等函數的時候,先將var doc = document 緩存起來,這樣 doc 就不是全局變量了,而是 在 同一個 作用域中的值 
  3. 通過原型鏈增加方法
  4. 創建 多個 dom 節點的時候,可以使用 document.createElementFrment 或者 先使用字符串拼接,再一次性 innerhtml
  5. 使用 字面量的 方法創建一個 數組或者對象,而不是使用 new Object 或者 new Array 
  6. 在數組的循環中 forEach 的 運行速度 > for > for in

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章