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

          通過【垃圾回收機制】的角度認識【Map與WeakMap】的區(qū)別

          共 4601字,需瀏覽 10分鐘

           ·

          2021-03-06 12:35


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

          簡單介紹JavaScript中的垃圾回收

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

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

          對于開發(fā)者來說,JavaScript 的內存管理是自動的、無形的。

          于是乎對于學習WeakMap這個概念需要用到垃圾回收相關知識,

          先簡單描述下垃圾回收是如何進行。

          例子:“我有一個朋友”

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

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

          這里我們在全局空間聲明了一位“朋友” myFriend

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

          myFriend = null;
          // 此時我們已經沒有這位朋友了

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

          從 根(window/global) 開始 查詢 找不到 其引用,此時垃圾回收機制會把它當作垃圾進行自動回收,并釋放內存。

          多個引用

          現(xiàn)在我們再舉一個例子,聲明多個變量指向同一個引用

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

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

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

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

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

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

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

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

          所以垃圾回收機制不會將它進行回收。

          回收策略

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

          策略的大意即為:

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

          • 垃圾回收階段的時候,將沒有標記進行清除。

          這里回收策略不是本章重點,具體策略在底層代碼上還會再細分,以及內存分代對應具體算法,暫時不展開講,有興趣可以查閱樸靈《深入淺出Node.js》以及V8垃圾回收機制相關文章。

          JavaScript中Map與WeakMap

          上面贅述那么多終于來講本文關鍵了,

          提前講上文原因在于WeakMap 的特點與垃圾回收機制有關。

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

          ECMAScript6新增的”弱映射“(WeakMap)是一種新的集合類型,為這門語言帶來了增強的鍵值對存儲機制。WeakMap是Map的”兄弟“類型,其API也是Map的子集。WeakMap中的”weak“(弱),描述的是JavaScript垃圾回收程序對待的”弱映射“中鍵的方式。---- 《JavaScript高級程序設計(第四版)》6.5

          嗯,說得好棒很詳細的樣子!

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

          Map與WeakMap簡單區(qū)別

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

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

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

          換言之,WeakMap所構建的實例中,

          key鍵所對應引用地址的引用斷開不屬于指向同一個內存地址的時候,

          其對應value值就會被加入垃圾回收隊伍。

          (粗暴理解為:因為key必須是個引用類型,當key引用斷了或變了,這個鍵值對就可以進垃圾桶了)

          觀察內存空間理解WeakMap

          因為通常條件下很難察覺WeakMap里面keyValue什么時候消失

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

          我們可以通過觀察內存的變化來觀察WeakMap的特性

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

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

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

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

          // 此時把 key鍵 的引用進行斷開,并觀察內存占用情況
          key = null;
          // key = new Array();
          // 這種改變引用地址寫法也可以引起 弱映射,因為引用地址不再是同塊內存地址 WeakMap內對應的value也會被垃圾回收

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

          $ node --expose-gc index.js

          第一次垃圾回收當前內存使用情況1.66MB
          第二次垃圾回收當前內存使用情況18.45MB
          第三次垃圾回收當前內存使用情況1.84MB

          那么我們來看看Map的情況,

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

          看看終端輸出效果

          $ node --expose-gc index.js
          第一次垃圾回收,當前內存使用情況:1.66MB
          第二次垃圾回收,當前內存使用情況:18.45MB
          第三次垃圾回收,當前內存使用情況:18.44MB

          很明顯我們將key = null的引用地址斷開后 ,

          value 仍然存在Map所構建的實例里面,一如既往還在內存里面。

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

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

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

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

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

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

          由此可見Map所構建的實例是需要手動清理,才能被垃圾回收清除,

          WeakMap只要外部的引用消失,所對應的鍵值對就會自動被垃圾回收清除。

          總結

          通過堆內存分析后重新認識MapWeakMap

          由于一開始接觸這個API的時候有點陌生,

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

          因為弱引用反正他就會被自動回收就是了不會占用內存balabala,總之就是一兩句話帶過

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

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

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

          最后看到新版《JavaScript高級程序設計(第四版)》對WeakMap描述也是簡單帶過,

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


          第一次產出文章,有表達描述不到位,或者代碼邏輯錯誤的地方歡迎指出。

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


          最后


          • 歡迎加我微信(winty230),拉你進技術群,長期交流學習...

          • 歡迎關注「前端Q」,認真學前端,做個專業(yè)的技術人...

          點個在看支持我吧
          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  午夜肏屄 | 草精品在线观看 | 无码一区二区三区在线观看 | 女人18毛片水真多免费 | 任我操视频在线 |