<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>

          高頻面試題:通過【垃圾回收機(jī)制】的角度認(rèn)識(shí)【Map與WeakMap】的區(qū)別

          共 4596字,需瀏覽 10分鐘

           ·

          2021-01-08 08:49

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

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

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

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

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

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

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

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

          沒有什么是用例子解決不了的

          // 從前,我有一個(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)沒有這位朋友了

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

          從?根(window/global)?開始 查詢 找不到 其引用,此時(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;
          你說的這個(gè)朋友是不是你.jpg

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

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

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

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

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

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

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

          回收策略

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

          策略的大意即為:

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

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

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

          JavaScript中Map與WeakMap

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

          示例使用的Node.js的進(jìn)程Api?process.memoryUsage()配合手動(dòng)垃圾回收global.gc()在終端觀察,
          也可以使用Chrome瀏覽器Performance功能錄制內(nè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,heapTotalheapUsed,external,arrayBuffers;
          其中主要屬性是?heapTotalheapUsed對(duì)應(yīng)的是V8的堆內(nèi)存信息。
          heapTotal是堆中總共申請(qǐng)的內(nèi)存量,heapUsed表示目前堆中使用的內(nèi)存量。單位都為字節(jié)

          現(xiàn)在我們通過代碼來展示

          // index.js
          // 第一次手動(dòng)清理垃圾以確保為最新狀態(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)行斷開,并觀察內(nèi)存占用情況
          key = null;
          // key = new Array();
          // 這種改變引用地址寫法也可以引起 弱映射,因?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

          那么我們來看看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的引用地址斷開后 ,

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

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

          // index.js
          // 第一次手動(dòng)清理垃圾以確保為最新狀態(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)行斷開,并觀察內(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

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

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

          總結(jié)

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

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

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

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

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

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

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

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

          于是下定決心來寫這一篇文章。


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

          結(jié)束語

          如果你對(duì)我的解釋有疑問,請(qǐng)給我留言,歡迎大家一起討論這些JS難題。


          本文完~

          喜歡本文的朋友,歡迎關(guān)注公眾號(hào)?前端陽光,收看更多精彩內(nèi)容

          點(diǎn)個(gè)[在看],是對(duì)我最大的支持!


          如果覺得這篇文章還不錯(cuò),來個(gè)【分享、點(diǎn)贊、在看】三連吧,讓更多的人也看到~


          瀏覽 49
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  天天爱天天操 | 色婷婷欧美在线播放内射 | 免费网站成人 视频 | 日日夜夜免费看三级片 | 亚洲色爽视频 |