1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 4900字,需瀏覽 10分鐘

         ·

        2021-04-27 01:33

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

        垃圾回收機(jī)制

        我們知道,程序運(yùn)行中會(huì)有一些垃圾數(shù)據(jù)不再使用,需要及時(shí)釋放出去,如果我們沒有及時(shí)釋放,這就是內(nèi)存泄露

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

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

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

        • 引用計(jì)數(shù)
        • 標(biāo)記清除

        引用計(jì)數(shù)

        最早最簡(jiǎn)單的垃圾回收機(jī)制,就是給一個(gè)占用物理空間的對(duì)象附加一個(gè)引用計(jì)數(shù)器,當(dāng)有其它對(duì)象引用這個(gè)對(duì)象時(shí),這個(gè)對(duì)象的引用計(jì)數(shù)加一,反之解除時(shí)就減一,當(dāng)該對(duì)象引用計(jì)數(shù)為 0 時(shí)就會(huì)被回收。

        該方式很簡(jiǎn)單,但會(huì)引起內(nèi)存泄漏:

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

        這種情況下每次調(diào)用 temp 函數(shù),ab 的引用計(jì)數(shù)都是 2 ,會(huì)使這部分內(nèi)存永遠(yuǎn)不會(huì)被釋放,即內(nèi)存泄漏。現(xiàn)在已經(jīng)很少使用了,只有低版本的 IE 使用這種方式。

        標(biāo)記清除

        V8 中主垃圾回收器就采用標(biāo)記清除法進(jìn)行垃圾回收。主要流程如下:

        • 標(biāo)記:遍歷調(diào)用棧,看老生代區(qū)域堆中的對(duì)象是否被引用,被引用的對(duì)象標(biāo)記為活動(dòng)對(duì)象,沒有被引用的對(duì)象(待清理)標(biāo)記為垃圾數(shù)據(jù)。
        • 垃圾清理:將所有垃圾數(shù)據(jù)清理掉

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

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

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

        a = null// 引用設(shè)置為 null
        // {} 將會(huì)被從內(nèi)存里清理出去

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

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

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

        如果作為 Map 的鍵喃?

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

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

        如果想讓 a 置為 null 時(shí),該對(duì)象被回收,該怎么做喃?

        WeakMap vs Map

        ES6 考慮到了這一點(diǎn),推出了:WeakMap 。它對(duì)于值的引用都是不計(jì)入垃圾回收機(jī)制的,所以名字里面才會(huì)有一個(gè)"Weak",表示這是弱引用(對(duì)對(duì)象的弱引用是指當(dāng)該對(duì)象應(yīng)該被GC回收時(shí)不會(huì)阻止GC的回收行為)。

        Map 相對(duì)于 WeakMap

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

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

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

        a = null

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

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

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

        console.log(usedSize()); // 1 初始狀態(tài),執(zhí)行g(shù)c()和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,為一個(gè) 5*1024*1024 的數(shù)組后,heapUsed為41.82M左右

        b = null;
        global.gc();

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

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

        其中,--expose-gc 參數(shù)表示允許手動(dòng)執(zhí)行垃圾回收機(jī)制

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

        global.gc(); // 0 每次查詢內(nèi)存都先執(zhí)行g(shù)c()再memoryUsage(),是為了確保垃圾回收,保證獲取的內(nèi)存使用狀態(tài)準(zhǔn)確
        console.log(usedSize()); // 1 初始狀態(tài),執(zhí)行g(shù)c()和 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,為一個(gè) 5*1024*1024 的數(shù)組后,heapUsed為41.82M左右

        b = null;
        global.gc();

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

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

        上面代碼中,只要外部的引用消失,WeakMap 內(nèi)部的引用,就會(huì)自動(dòng)被垃圾回收清除。由此可見,有了它的幫助,解決內(nèi)存泄漏就會(huì)簡(jiǎn)單很多。

        最后看一下 WeakMap

        WeakMap

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

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

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

        屬性:

        • constructor:構(gòu)造函數(shù)

        方法:

        • has(key):判斷是否有 key 關(guān)聯(lián)對(duì)象
        • get(key):返回key關(guān)聯(lián)對(duì)象(沒有則則返回 undefined)
        • set(key):設(shè)置一組key關(guān)聯(lián)對(duì)象
        • delete(key):移除 key 的關(guān)聯(lián)對(duì)象
        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 都是弱引用,可以被垃圾回收機(jī)制回收,可以用來保存DOM節(jié)點(diǎn),不容易造成內(nèi)存泄漏

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

        參考

        你不知道的 WeakMap

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

        最后

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

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            99热18 | 韩国无遮挡18禁啪啪成人 | 欧美激情五月天 | 精工厂成人 免费网站观观看 | 8v天堂国产在线一区二区 | av亚洲精华国产精华精华液 | 人人摸人人插屄屄 | 日韩欧美色图 | 欧美日韩一二三四 | www.奇米 |