<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          WeakMap 和 Map 的區(qū)別,WeakMap 原理,為什么能被 GC?

          共 4900字,需瀏覽 10分鐘

           ·

          2021-04-27 01:33

          面試官也在看的前端面試資料

          垃圾回收機制

          我們知道,程序運行中會有一些垃圾數據不再使用,需要及時釋放出去,如果我們沒有及時釋放,這就是內存泄露

          JS 中的垃圾數據都是由垃圾回收(Garbage Collection,縮寫為 GC)器自動回收的,不需要手動釋放,它是如何做的喃?

          很簡單,JS 引擎中有一個后臺進程稱為垃圾回收器,它監(jiān)視所有對象,觀察對象是否可被訪問,然后按照固定的時間間隔周期性的刪除掉那些不可訪問的對象即可

          現在各大瀏覽器通常用采用的垃圾回收有兩種方法:

          • 引用計數
          • 標記清除

          引用計數

          最早最簡單的垃圾回收機制,就是給一個占用物理空間的對象附加一個引用計數器,當有其它對象引用這個對象時,這個對象的引用計數加一,反之解除時就減一,當該對象引用計數為 0 時就會被回收。

          該方式很簡單,但會引起內存泄漏:

          // 循環(huán)引用的問題
          function temp(){
              var a={};
              var b={};
              a.o = b;
              b.o = a;
          }

          這種情況下每次調用 temp 函數,ab 的引用計數都是 2 ,會使這部分內存永遠不會被釋放,即內存泄漏。現在已經很少使用了,只有低版本的 IE 使用這種方式。

          標記清除

          V8 中主垃圾回收器就采用標記清除法進行垃圾回收。主要流程如下:

          • 標記:遍歷調用棧,看老生代區(qū)域堆中的對象是否被引用,被引用的對象標記為活動對象,沒有被引用的對象(待清理)標記為垃圾數據。
          • 垃圾清理:將所有垃圾數據清理掉

          (圖片來源:How JavaScript works: memory management + how to handle 4 common memory leaks)

          在我們的開發(fā)過程中,如果我們想要讓垃圾回收器回收某一對象,就將對象的引用直接設置為 null

          var a = {}; // {} 可訪問,a 是其引用

          a = null// 引用設置為 null
          // {} 將會被從內存里清理出去

          但如果一個對象被多次引用時,例如作為另一對象的鍵、值或子元素時,將該對象引用設置為 null 時,該對象是不會被回收的,依然存在

          var a = {}; 
          var arr = [a];

          a = null
          console.log(arr)
          // [{}]

          如果作為 Map 的鍵喃?

          var a = {}; 
          var map = new Map();
          map.set(a, '三分鐘學前端')

          a = null
          console.log(map.keys()) // MapIterator {{}}
          console.log(map.values()) // MapIterator {"三分鐘學前端"}

          如果想讓 a 置為 null 時,該對象被回收,該怎么做喃?

          WeakMap vs Map

          ES6 考慮到了這一點,推出了:WeakMap 。它對于值的引用都是不計入垃圾回收機制的,所以名字里面才會有一個"Weak",表示這是弱引用(對對象的弱引用是指當該對象應該被GC回收時不會阻止GC的回收行為)。

          Map 相對于 WeakMap

          • Map 的鍵可以是任意類型,WeakMap 只接受對象作為鍵(null除外),不接受其他類型的值作為鍵
          • Map 的鍵實際上是跟內存地址綁定的,只要內存地址不一樣,就視為兩個鍵;WeakMap 的鍵是弱引用,鍵所指向的對象可以被垃圾回收,此時鍵是無效的
          • Map 可以被遍歷, WeakMap 不能被遍歷

          下面以 WeakMap 為例,看看它是怎么上面問題的:

          var a = {}; 
          var map = new WeakMap();
          map.set(a, '三分鐘學前端')
          map.get(a)

          a = null

          上例并不能看出什么?我們通過 process.memoryUsage 測試一下:

          //map.js
          global.gc(); // 0 每次查詢內存都先執(zhí)行gc()再memoryUsage(),是為了確保垃圾回收,保證獲取的內存使用狀態(tài)準確

          function usedSize({
              const used = process.memoryUsage().heapUsed;
              return Math.round((used / 1024 / 1024) * 100) / 100 + "M";
          }

          console.log(usedSize()); // 1 初始狀態(tài),執(zhí)行gc()和memoryUsage()以后,heapUsed 值為 1.64M

          var map = new Map();
          var b = new Array(5 * 1024 * 1024);

          map.set(b, 1);

          global.gc();
          console.log(usedSize()); // 2 在 Map 中加入元素b,為一個 5*1024*1024 的數組后,heapUsed為41.82M左右

          b = null;
          global.gc();

          console.log(usedSize()); // 3 將b置為空以后,heapUsed 仍為41.82M,說明Map中的那個長度為5*1024*1024的數組依然存在

          執(zhí)行 node --expose-gc map.js 命令:

          其中,--expose-gc 參數表示允許手動執(zhí)行垃圾回收機制

          // weakmap.js
          function usedSize({
              const used = process.memoryUsage().heapUsed;
              return Math.round((used / 1024 / 1024) * 100) / 100 + "M";
          }

          global.gc(); // 0 每次查詢內存都先執(zhí)行gc()再memoryUsage(),是為了確保垃圾回收,保證獲取的內存使用狀態(tài)準確
          console.log(usedSize()); // 1 初始狀態(tài),執(zhí)行gc()和 memoryUsage()以后,heapUsed 值為 1.64M
          var map = new WeakMap();
          var b = new Array(5 * 1024 * 1024);

          map.set(b, 1);

          global.gc();
          console.log(usedSize()); // 2 在 Map 中加入元素b,為一個 5*1024*1024 的數組后,heapUsed為41.82M左右

          b = null;
          global.gc();

          console.log(usedSize()); // 3 將b置為空以后,heapUsed 變成了1.82M左右,說明WeakMap中的那個長度為5*1024*1024的數組被銷毀了

          執(zhí)行 node --expose-gc weakmap.js 命令:

          上面代碼中,只要外部的引用消失,WeakMap 內部的引用,就會自動被垃圾回收清除。由此可見,有了它的幫助,解決內存泄漏就會簡單很多。

          最后看一下 WeakMap

          WeakMap

          WeakMap 對象是一組鍵值對的集合,其中的鍵是弱引用對象,而值可以是任意

          注意,WeakMap 弱引用的只是鍵名,而不是鍵值。鍵值依然是正常引用。

          WeakMap 中,每個鍵對自己所引用對象的引用都是弱引用,在沒有其他引用和該鍵引用同一對象,這個對象將會被垃圾回收(相應的key則變成無效的),所以,WeakMap 的 key 是不可枚舉的。

          屬性:

          • constructor:構造函數

          方法:

          • has(key):判斷是否有 key 關聯對象
          • get(key):返回key關聯對象(沒有則則返回 undefined)
          • set(key):設置一組key關聯對象
          • delete(key):移除 key 的關聯對象
          let myElement = document.getElementById('logo');
          let myWeakmap = new WeakMap();

          myWeakmap.set(myElement, {timesClicked0});

          myElement.addEventListener('click'function({
            let logoData = myWeakmap.get(myElement);
            logoData.timesClicked++;
          }, false);

          除了 WeakMap 還有 WeakSet 都是弱引用,可以被垃圾回收機制回收,可以用來保存DOM節(jié)點,不容易造成內存泄漏

          另外還有 ES12 的 WeakRef ,感興趣的可以了解下,今晚太晚了,之后更新

          參考

          你不知道的 WeakMap

          來自:https://github.com/Advanced-Frontend/Daily-Interview-Question

          最后

          歡迎關注「三分鐘學前端」,回復「交流」自動加入前端三分鐘進階群,每日一道編程算法題(第二天解答),助力你成為更優(yōu)秀的前端開發(fā)!

          》》面試官也在看的前端面試資料《《
          “在看和轉發(fā)”就是最大的支持
          瀏覽 18
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人大香蕉| 黄色日本网站 | 三级在线视频播放 | 五月丁香六月久久 | 亚洲制服在线观看 |