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>

        通過(guò)【垃圾回收機(jī)制】的角度認(rèn)識(shí)【Map與WeakMap】的區(qū)別

        共 4491字,需瀏覽 9分鐘

         ·

        2021-02-19 13:44

        原文鏈接: https://www.zhihu.com/people/yiqun-a-qia
        前言:前段時(shí)間剛接觸WeakMap時(shí)概念很模糊,查了很多文章大部分是簡(jiǎn)單掠過(guò),后來(lái)通過(guò)學(xué)習(xí)了垃圾回收機(jī)制后有感而發(fā)。

        簡(jiǎn)單介紹JavaScript中的垃圾回收

        JavaScript是使用垃圾回收的語(yǔ)言,也就是說(shuō)執(zhí)行環(huán)境負(fù)責(zé)在代碼執(zhí)行時(shí)管理內(nèi)存。

        眾所周知,我們一般使用 javascript 進(jìn)行開(kāi)始都很少關(guān)注垃圾回收是如何進(jìn)行的,

        對(duì)于開(kāi)發(fā)者來(lái)說(shuō),JavaScript 的內(nèi)存管理是自動(dòng)的、無(wú)形的。

        于是乎對(duì)于學(xué)習(xí)WeakMap這個(gè)概念需要用到垃圾回收相關(guān)知識(shí),

        先簡(jiǎn)單描述下垃圾回收是如何進(jìn)行。

        例子:“我有一個(gè)朋友”

        沒(méi)有什么是用例子解決不了的

        // 從前,我有一個(gè)朋友。
        let myFriend = {
        info: "I have a friend",
        attr: "Lsp"
        }

        這里我們?cè)谌挚臻g聲明了一位“朋友”?myFriend

        他的引用地址指向了{ info: "I have a friend", attr: "Lsp" }

        myFriend = null;
        // 此時(shí)我們已經(jīng)沒(méi)有這位朋友了

        由于我們與這位“朋友”斷了聯(lián)系,

        從?根(window/global)?開(kāi)始 查詢(xún) 找不到 其引用,此時(shí)垃圾回收機(jī)制會(huì)把它當(dāng)作垃圾進(jìn)行自動(dòng)回收,并釋放內(nèi)存。

        多個(gè)引用

        現(xiàn)在我們?cè)倥e一個(gè)例子,聲明多個(gè)變量指向同一個(gè)引用

        // 大家好,我是...
        let me = null;

        // 然后,我有一個(gè)朋友他...
        let myFriend = {
        info: "I have a friend",
        attr: "Lsp"
        }
        // (....?)
        me = myFriend;
        你說(shuō)的這個(gè)朋友是不是你.jpg

        由此可見(jiàn)目前有兩個(gè)變量引用地址指向了同一塊地方?{ info: "I have a friend", attr: "Lsp" }。

        我們現(xiàn)在將其中一個(gè)變量進(jìn)行斷開(kāi)。

        // 我說(shuō)的那位朋友真的不是我!
        me = null;

        即使將其中一個(gè)變量的引用地址斷開(kāi)

        另外一個(gè) "朋友"變量 任然繼續(xù)引用

        (也就是說(shuō)對(duì)象還是可以通過(guò)查找到)

        所以垃圾回收機(jī)制不會(huì)將它進(jìn)行回收。

        回收策略

        JavaScript最常用垃圾回收策略是"標(biāo)記清理(mark-and-sweep)"

        策略的大意即為:

        • 遍歷空間下所有的對(duì)象,并標(biāo)記活著的,有被引用的并且最終可以到達(dá)根(window/global)的對(duì)象。

        • 垃圾回收階段的時(shí)候,將沒(méi)有標(biāo)記進(jìn)行清除。

        這里回收策略不是本章重點(diǎn),具體策略在底層代碼上還會(huì)再細(xì)分,以及內(nèi)存分代對(duì)應(yīng)具體算法,暫時(shí)不展開(kāi)講,有興趣可以查閱樸靈《深入淺出Node.js》以及V8垃圾回收機(jī)制相關(guān)文章。

        JavaScript中Map與WeakMap

        上面贅述那么多終于來(lái)講本文關(guān)鍵了,

        提前講上文原因在于WeakMap?的特點(diǎn)與垃圾回收機(jī)制有關(guān)。

        我們引用一下《JavaScript高級(jí)程序設(shè)計(jì)(第四版)》的原話(huà)。

        ECMAScript6新增的”弱映射“(WeakMap)是一種新的集合類(lèi)型,為這門(mén)語(yǔ)言帶來(lái)了增強(qiáng)的鍵值對(duì)存儲(chǔ)機(jī)制。WeakMap是Map的”兄弟“類(lèi)型,其API也是Map的子集。WeakMap中的”weak“(弱),描述的是JavaScript垃圾回收程序?qū)Υ摹比跤成洹爸墟I的方式。---- 《JavaScript高級(jí)程序設(shè)計(jì)(第四版)》6.5

        嗯,說(shuō)得好棒很詳細(xì)的樣子!

        可是我完全不懂呢( 嗯嗯嗯我完全理解了呢.jpg

        Map與WeakMap簡(jiǎn)單區(qū)別

        • Map的鍵值可以是原始數(shù)據(jù)類(lèi)型和引用類(lèi)型,WeakMap的鍵值只能說(shuō)引用類(lèi)型(object)

        • Map可以迭代遍歷鍵,WeakMap不可迭代遍歷鍵

        WeakMap中的”weak“表示弱映射的鍵是”弱弱地拿著“的,意思就是,這些鍵不屬于正式的引用。

        換言之,WeakMap所構(gòu)建的實(shí)例中,

        key鍵所對(duì)應(yīng)引用地址的引用斷開(kāi)不屬于指向同一個(gè)內(nèi)存地址的時(shí)候,

        其對(duì)應(yīng)value值就會(huì)被加入垃圾回收隊(duì)伍。

        (粗暴理解為:因?yàn)閗ey必須是個(gè)引用類(lèi)型,當(dāng)key引用斷了或變了,這個(gè)鍵值對(duì)就可以進(jìn)垃圾桶了)

        觀察內(nèi)存空間理解WeakMap

        因?yàn)橥ǔl件下很難察覺(jué)WeakMap里面keyValue什么時(shí)候消失

        但是通過(guò)某一個(gè)引用類(lèi)型的值大到足夠占據(jù)一定內(nèi)存時(shí)候

        我們可以通過(guò)觀察內(nèi)存的變化來(lái)觀察WeakMap的特性

        示例使用的Node.js的進(jìn)程Api?process.memoryUsage()配合手動(dòng)垃圾回收global.gc()在終端觀察,
        也可以使用Chrome瀏覽器Performance功能錄制內(nèi)存變化,
        但是為了方便用代碼展示就依次展開(kāi)了,0 .0 本質(zhì)觀察到內(nèi)存變化即可,手段可以有多種。
        glabal.gc()
        手動(dòng)調(diào)用一次垃圾回收。需要在運(yùn)行js文件時(shí)候增加命令 --expose-gc,一般環(huán)境下不推薦使用,這里做學(xué)習(xí)用。
        process.memoryUsage()
        查看Node進(jìn)程的內(nèi)存占用情況。
        返回值為對(duì)象其中包含五個(gè)屬性 rss,heapTotal,heapUsed,external,arrayBuffers;
        其中主要屬性是?heapTotalheapUsed對(duì)應(yīng)的是V8的堆內(nèi)存信息。
        heapTotal是堆中總共申請(qǐng)的內(nèi)存量,heapUsed表示目前堆中使用的內(nèi)存量。單位都為字節(jié)。

        現(xiàn)在我們通過(guò)代碼來(lái)展示

        // index.js
        // 第一次手動(dòng)清理垃圾以確保為最新?tīng)顟B(tài),觀察內(nèi)存情況
        global.gc();
        console.log(`第一次垃圾回收,當(dāng)前內(nèi)存使用情況:${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}MB`);
        const wm = new WeakMap();

        let key = {};
        // 給 WeakMap實(shí)例 賦值一個(gè) 占領(lǐng)內(nèi)存足夠大的 鍵值對(duì)
        wm.set(key, new Array(114514 * 19));
        // 手動(dòng)清理一下垃圾 觀察內(nèi)存占用情況
        global.gc();
        console.log(`第二次垃圾回收,當(dāng)前內(nèi)存使用情況:${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}MB`);

        // 此時(shí)把 key鍵 的引用進(jìn)行斷開(kāi),并觀察內(nèi)存占用情況
        key = null;
        // key = new Array();
        // 這種改變引用地址寫(xiě)法也可以引起 弱映射,因?yàn)橐玫刂凡辉偈峭瑝K內(nèi)存地址 WeakMap內(nèi)對(duì)應(yīng)的value也會(huì)被垃圾回收

        global.gc();
        console.log(`第三次垃圾回收,當(dāng)前內(nèi)存使用情況:${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}MB`);

        $ node --expose-gc index.js

        第一次垃圾回收當(dāng)前內(nèi)存使用情況1.66MB
        第二次垃圾回收,當(dāng)前內(nèi)存使用情況18.45MB
        第三次垃圾回收,當(dāng)前內(nèi)存使用情況1.84MB

        那么我們來(lái)看看Map的情況,

        與上方index.js的代碼一致,把new WeakMap()換成new Map()。

        看看終端輸出效果

        $ node --expose-gc index.js
        第一次垃圾回收,當(dāng)前內(nèi)存使用情況:1.66MB
        第二次垃圾回收,當(dāng)前內(nèi)存使用情況:18.45MB
        第三次垃圾回收,當(dāng)前內(nèi)存使用情況:18.44MB

        很明顯我們將key = null的引用地址斷開(kāi)后 ,

        value?仍然存在Map所構(gòu)建的實(shí)例里面,一如既往還在內(nèi)存里面。

        現(xiàn)在我們將代碼場(chǎng)景改成Map的樣子

        // index.js
        // 第一次手動(dòng)清理垃圾以確保為最新?tīng)顟B(tài),觀察內(nèi)存情況
        global.gc();
        console.log(
        `第一次垃圾回收,當(dāng)前內(nèi)存使用情況:${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}MB`
        );
        const m = new Map();

        let key = {};
        m.set(key, new Array(114514 * 19));
        // 手動(dòng)清理一下垃圾 觀察內(nèi)存占用情況
        global.gc();
        console.log(
        `第二次垃圾回收,當(dāng)前內(nèi)存使用情況:${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}MB,
        當(dāng)前Map的長(zhǎng)度: ${m.size}`
        );

        // 此時(shí)把 key鍵 的引用進(jìn)行斷開(kāi),并觀察內(nèi)存占用情況
        key = null;
        global.gc();
        console.log(
        `第三次垃圾回收,當(dāng)前內(nèi)存使用情況:${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}MB,
        當(dāng)前Map的長(zhǎng)度: ${m.size}`
        );

        // 清除Map所有鍵值對(duì)
        m.clear();

        global.gc();
        console.log(
        `第四次垃圾回收,當(dāng)前內(nèi)存使用情況:${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}MB,
        當(dāng)前Map的長(zhǎng)度: ${m.size}`
        );
        $ node --expose-gc index.js
        第一次垃圾回收,當(dāng)前內(nèi)存使用情況1.66MB
        第二次垃圾回收,當(dāng)前內(nèi)存使用情況18.45MB,當(dāng)前Map的長(zhǎng)度: 1
        第三次垃圾回收當(dāng)前內(nèi)存使用情況18.45MB,當(dāng)前Map的長(zhǎng)度: 1
        第四次垃圾回收,當(dāng)前內(nèi)存使用情況1.85MB當(dāng)前Map的長(zhǎng)度: 0

        由此可見(jiàn)Map所構(gòu)建的實(shí)例是需要手動(dòng)清理,才能被垃圾回收清除,

        WeakMap只要外部的引用消失,所對(duì)應(yīng)的鍵值對(duì)就會(huì)自動(dòng)被垃圾回收清除。

        總結(jié)

        通過(guò)堆內(nèi)存分析后重新認(rèn)識(shí)MapWeakMap,

        由于一開(kāi)始接觸這個(gè)API的時(shí)候有點(diǎn)陌生,

        查閱很多網(wǎng)上很多文章后描述的樣子非常表層一筆帶過(guò)

        因?yàn)槿跻梅凑蜁?huì)被自動(dòng)回收就是了不會(huì)占用內(nèi)存balabala,總之就是一兩句話(huà)帶過(guò)

        這讓我很頭疼,下定決定翻了很多的書(shū)結(jié)合理解才搞懂WeakMap存在的意義。

        前期是在在《現(xiàn)代JavaScript教程》受到啟發(fā),

        通過(guò)翻查《深入淺出Node.js》找到了驗(yàn)證方法,

        最后看到新版《JavaScript高級(jí)程序設(shè)計(jì)(第四版)》對(duì)WeakMap描述也是簡(jiǎn)單帶過(guò),

        于是下定決心來(lái)寫(xiě)這一篇文章。


        第一次產(chǎn)出文章,有表達(dá)描述不到位,或者代碼邏輯錯(cuò)誤的地方歡迎指出。

        我是阿卡,一名普通搬磚的前端工程師。

        瀏覽 58
        點(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>
            久热精品视频在线播放 | 激情变态少妇视频 | 精品无人区一区二区三区88AV | 久久国产精品免费 | 国产传媒在线看 | metart精品嫩模asspics | 91丨牛牛丨国产人妻 | 美女被操的视频在线观看 | 午夜精品久久久久久久99蜜桃乐播 | 国精产品一区二区三区mba |